0% found this document useful (0 votes)
329 views

Machine Language For The Commodore Revised and Expanded Edition PDF

Uploaded by

Dusun Panembong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
329 views

Machine Language For The Commodore Revised and Expanded Edition PDF

Uploaded by

Dusun Panembong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 396

/////////////////////////////////m

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

Copyright © 1986 by Brady Communications Company, Inc.


All rights reserved
including the right of reproduction
in whole or in part in any form

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

PRENTICE HALL PRESS is a trademark of Simon & Schuster, Inc.

Manufactured in the United States of America

123456789 10

Library of Congress Cataloging in Publication Data

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

4 Numbers, Arithmetic, and Subroutines 57


Numbers: Signed and Unsigned
Big Numbers: Multiple Bytes
Addition
Subtraction
Comparing Numbers
Multiplication
Subroutines
The 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

6 Linking BASIC and Machine Language 91


Siting the Program
BASIC Memory Layout
The Wicked SOV
BASIC Variables
Exchanging Data: BASIC and Machine Language
Things You Have Learned
Questions and Projects

7 Stack, USR, Interrupt, and Wedge 111


A Brief Intermission
Temporary Storage: The Stack
Interrupts and RTI
USR: A Brother to SYS
Interrupts: NMI, IRQ, and BRK
An Interrupt Project
The IA Chips: PIA, VIA, and CIA
Infiltrating BASIC: The Wedge
Project: Adding a Command
Things You Have Learned
Questions and Projects

8 Timing, Input/Output, and Conclusion 131


Timing
Input and Output
A File Transfer Program
Review: The Instruction Set
Debugging
Symbolic Assemblers
What You Have Learned
Questions and Projects
Appendix A The 6502/6510/6509/7501/8500 Instruction
Set 147
Appendix B Some Characteristics of Commodore
Machines 155
Appendix C Memory Maps 167
Appendix D Character Sets 241
Appendix E Exercises for Alternative Commodore
Machines 251
Appendix F Floating Point Representation 277
Appendix G Uncrashing 279
Appendix H Supermon Instructions 283
Appendix I IA Chip Information 293
Appendix J Disk User's Guide 357
Glossary 365
Index 37i

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.

LIMITS OF LIABILITY AND


DISCLAIMER OF WARRANTY

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.

Note for Commodore 128 Owners

The Commodore 128 is three machines in one: a Commodore 64, a Commodore


128, and a CP/M machine. You may select any of the three at any time.
If you choose the Commodore 64 mode, you'll find examples within this book
that will work on your machine. The programs you write will be compatible with
other ("real") Commodore 64 computers. But you'll lose access to extra memory
and to other features of the new machine. In particular, you won't have a built-in
machine language monitor and will need to load one from tape or disk.
If you choose the Commodore 128 mode, you're working with a richer and more
powerful machine. You will have a built-in machine language monitor for speed
and convenience, and access to new features such as 80 columns, with extra
complexity. There are new rules to be learned. This book contains extra material
to enable you to cope with the new features of the C128.
If you choose CP/M mode, you will be in an environment that is quite different
from other Commodore machines. This book, working with the 64 or 128 mode,
can teach you principles of machine language and skills which may be carried to
other computer environments, including CP/M. But it will not teach you CP/M itself
or CP/M's machine language.

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:

• The inner workings of microcomputers


• Computer notation: binary and hexadecimal
• The 650x's inner architecture
• Beginning use of a machine language monitor
• A computer's "memory layout"
• First machine language commands
• Writing and entering a simple program
MACHINE LANGUAGE FOR COMMODORE MACHINES

The Inner Workings of Microcomputers


All computers contain a large number of electrical circuits. Within any
binary computer, these circuits may be in only two states: "on" or "off."

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

The last description, 1 or 0, is quite useful. It is compact and numeric. If


we had a group of eight circuits within the computer, some of which were
"on" and others "off," we could describe their conditions with an expression
such as:

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

Figure 1.1 Address bus connecting 650x & 3 chips


4 MACHINE LANGUAGE FOR COMMODORE MACHINES

that chip to be selected; it prepares to communicate with the 650x. All


other chips say, "That's not me!" and will not participate in data transfer.

The Data Bus


Once the 650x microprocessor has sent an address over the address bus
and it has been recognized by a memory chip, data may flow between
memory and 650x. This data is eight bits (it flows over eight wires). It
might look like this:

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)

Figure 1.2 Two-way data bus

All other chips are still connected to the data bus, but they have not been
selected, so they ignore the information.

The address bus is accompanied by a few extra wires (sometimes called


FIRST CONCEPTS

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.

It is often convenient to refer to an address as a decimal number. This is


especially true for PEEK and POKE statements in the BASIC language.
We may do this by giving each bit a "weight." Bit zero (at the right) has
a weight of 1; each bit to the left has a weight of double the amount, so
that bit 15 (at the left) has a weight of 32768. Thus, a binary address such
as

DDD1DD1D1D1D11DD

has a value of 4096 + 512+128 + 32 + 8 + 4 or 4780. A POKE to 4780


decimal would use the above binary address to reach the correct part of
memory.

128 64 32 16 8 4 2 1

EIGHT BITS

XXX

32768 16384 8192 4096 20481024 512 256 128 64 32 16 8 4

SIXTEEN BITS

Figure 1.3

Direct conversion between decimal and binary is seldom needed. Such


conversions usually pass through an intermediate number system, called
hexadecimal.

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

I use for some activity?", an answer such as "Address


&DDDlDDlDlDlDllDDlf might be correct but would probably be un
satisfactory. There are too many digits.

Hexadecimal is a code used by humans to conveniently represent binary


numbers. The computer uses binary, not hexadecimal; programmers use
hexadecimal because binary is cumbersome.

To represent a binary number in hexadecimal, the bits must be grouped


together four at a time. If we take the binary value given above and split
it into groups of four, we get

DDD1 DD10 ID ID HDD

Now each group of four bits is represented by a digit as shown in the


following table:

DDDD-D D10D-4 IDDO-fl 1100-C


0DD1-1 0101-5 lOOl-R 1101-D
DD1D-2 DllD-t 1010-A 1110-E
0011-3 0111-7 1011-B 1111-F

Thus, the number would be represented as hexadecimal IE AC. A dollar


sign is often prefixed to a hexadecimal number so that it may be clearly
recognized:$12AC.

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.

Eight-bit numbers are represented with two hexadecimal digits. Thus,


SDl'DllDll may be written as $5B.

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.

The formal procedure, or algorithm, to go from hex to decimal is as follows.


Step 1: Take the leftmost digit; if it's a letter A to F, convert it to the appropriate
numeric value (A equals ID, B equals 11, and so on).
Step 2: If there are no more digits, you're finished; you have the number. Stop.
Step 3: Multiply the value so far by sixteen. Add the next digit to the result,
converting letters if needed. Go back to step 2.

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.

This is easy to do by hand or with a calculator.

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:

.DQDD-D .55DD-4 .5DDD-fl .75DD-C


.Dt55-1 .3155-5 .5kS5-q .fllS5-D
.1550-5 .375D-b .fcS5D-A .fl75D-E
.1375-3 .4375-7 .tfl?5-B .R375-F

If we were to translate 4780 using this method, we would divide by 16,


giving 298.75. The fraction tells us the last digit is C; we now divide 298
by 16, giving 18.625. The fraction corresponds to A, making the last two
digits AC. Next we divide 18 by 16, getting 1.125—now the last three
digits are E AC. We don't need to divide the one by 16, although that would
work; we just put it on the front of the number to get an answer of $ 15 AC.

There are other methods of performing decimal-to-hexadecimal conver-


MACHINE LANGUAGE FOR COMMODORE MACHINES

sions. You may wish to look them up in a book on number systems.


Alternatively, yog may wish to buy a calculator that does the job electron
ically. Some programmers get so experienced that they can do conver
sions in their heads; I call them "hex nuts."

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.

SP The stack pointer keeps track of a temporary


storage area.
We will talk about each of these registers in more detail later. At the
moment, we will concentrate on the PC (program counter).
10 MACHINE LANGUAGE FOR COMMODORE MACHINES

| 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.

The microprocessor now has the instruction, which tells it to do something.


The action is performed, and the whole action now repeats for the next

DATA BUS

Figure 1.6
FIRST CONCEPTS

instruction. In other words, address $1535 will be sent to memory, and


the PC will be incremented to $lE3t.

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.

Data Registers:A, X, and Y


Any of three registers can be used to hold and manipulate eight bits of
data. We may load information from memory into A, X, or Y; and we may
store information into memory from any of A, X, or Y.

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.

First Program Project


C128 note: The programming task that follows will need to be slightly
changed if you are using a Commodore 128 in C128 mode. In particular,
the program will need to be written into a different part of memory from
that which is shown below. Check Appendix E, Exercises for the Com-
modore C128, page 251 for the correct C128 coding. ra«^oB 80*oB8f
Here's a programming task: locations $D3flD and $D3fll contain in
formation. We wish to write a program to exchange the contents of the
two locations. How can we do this?

We must make up a plan. We know that we cannot transfer information


directly from memory to memory. We must load to a register, and then
store. But there's more. We must not store and destroy data in memory
until that data has been safely put away. How can we do this?
12 MACHINE LANGUAGE FOR COMMODORE MACHINES

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:

LDA $D3aD (bring in first value)


LDX $D3ai (bring in second value)
STA $D3a1 (store in opposite place)
STX $D3aD (and again)

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.

We have written our program in a notation styled for human readability,


called assembly language. But the computer doesn't understand this no
tation. We must translate it to machine language.

The binary code forLDAis^lDlDHDl, or hexadecimal AD. That's


what the computer recognizes; that's the instruction we must place in
memory. So we code the first line:

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

This method of storing addresses—low byte first—is standard in the 650x.


It seems unusual, but it's there for a good reason. That is, the computer
gets extra speed from this "backwards" address. Get used to it; you'll see
it again, many times.

Here are some machine language op codes for the instructions we may
use. You do not need to memorize them.

LDA-AD LDX-AE LDY-AC BRK-DD


STA-flD STX-flE STY-flC

>w we car1 complete the translation of our program.

AD flD 03 LDA $D3flD


AE fll 03 LDX $D3fll
flD fll D3 STA $03fll
flE flD D3 STX $D3flD
DO BRK

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

Monitors: What They Are


All computers have a built-in set of programs called an operating system
that gives the machine its style and basic capabilities. The operating sys
tem takes care of communications—reading the keyboard, making the
proper things appear on the screen, and transferring data between the
computer and other devices, such as disk, tape, or printer.

When we type on the computer keyboard, we use the operating system,


which detects the characters we type. But there's an extra set of programs
built into the computer that must decide what we mean. When we are
using the BASIC language, we'll be communicating with the BASIC mon
itor, which understands BASIC commands such as NEW, LORD, LIST,
or RUN. It contains editing features that allow us to change the BASIC
program that we are writing.

But when we switch to another system—often another language—we'll


need to use a different monitor. Commands such as NEW or LIST don't
have any meaning for a machine language program. We must leave the
BASIC monitor and enter a new environment: the machine language mon
itor. We'll need to learn some new commands because we will be com
municating with the computer in a different way.

The Machine Language Monitor


Most PET/CBM computers have a simple MLM (machine language mon
itor) built in. It may be extended with extra commands. The Commodore
PLUS/4 contains a very powerful MLM. The VIC-20 and Commodore 64
do not have a built-in MLM, but one can be added. Such a monitor may
be either loaded into RAM or plugged in as a cartridge. Monitors may be
purchased or obtained from user clubs.

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.

Caution: Occasionally, you may run across a monitor which uses—and


changes—memory locations in the address range $D33C to $D3FD,
which is where we will put many of our programs. There is a version of
program MICROMON which does this. Such a monitor will create problems
for us as we try to work the following examples, since our programs and
data will be changed by the monitor as we use it. The built-in monitors
will certainly not have any problem. If you encounter any problems with
the following examples, and it appears that your program is being mys
teriously changed, switch to another machine language monitor.

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).

Remember: BASIC commands are no good in the MLM, and machine


language monitor commands (such as X) are no good in BASIC. At first,
you'll give the wrong commands at the wrong time because it's hard to
keep track of which monitor system is active. If you type in an MLM
command when you're in BASIC, you'll probably get a ?SYNT AX ERROR
reply. If you type in a BASIC command when you're in the machine lan
guage monitor, you'll probably get a question mark in the line you typed.

Some other MLM commands are as follows:

M 1DDD 1D1D (display memory from hex 1DDD to


1D1D)
R (display registers ... again!)
FIRST CONCEPTS 17

G D33C (go to D 3 3 C and start running a


program)

Do not enter this last (G) command. There is no program at address


$D33C yet, so the computer would execute random instructions and we
would lose control.

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.

Displaying Memory Contents


You'll notice that there is a command for displaying the contents of mem
ory, but there doesn't seem to be one for changing memory. You can do
both, of course.

Suppose we ask to display memory from $lDDOto$lDlD with the


command

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.

In the example above, $ 1D D D contains a value of$ll;$lDDl contains


a value of $3 A; and so on, until $1DD7, which contains a value of $AA.
We continue with address UDDfl on the next line. Most monitors show
eight memory locations on each line, although some VIC-20 monitors show
only five because of the narrow screen.

We asked for memory locations up to address $ 1D1 □ only; but we get


the contents of locations up to $1D17 in this case. The monitor always
fills out a line, even if you don't ask for the extra values.
18 MACHINE LANGUAGE FOR COMMODORE MACHINES

Changing Memory Contents


Once we have displayed the contents of part of memory, we can change
that part of memory easily. All we need to do is to move the cursor until
it is positioned over the memory contents in question, type over the value
displayed, and then press RETURN.

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.

If you have displayed the contents of memory, as in the example above,


you might like to change a number of locations to zero. Don't forget to
strike RETURN so that the change on the screen will take effect in mem
ory. Give another M memory display command to confirm that memory
has indeed been changed.

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.

Entering the Program


C128 note: Remember to check Exercises for the Commodore 128, in
Appendix E, for the appropriate code.

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).

D33C AD fiD D3 LDA $D3fiD


D33F AE fll D3 LDX $D3fll
Q34E flD fll D3 STA $D3fll
D345 flE flD D3 STX $D3flD
DD
FIRST CONCEPTS 19

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.

How do we put it in? Easy. We must change memory. So, we go to the


MLM, and display memory with

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:

.:D33C AD flD D3 RE fll D3 flD fll


D3 flE flD D3 DD xx xx xx

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

Remember to press RETURN. Now we may run our program; we start it


up with

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.

Things You Have Learned


—Computers use binary. If we want to work with the inner fabric of the computer,
we must come to terms with binary values.
—Hexadecimal notation is for humans, not for computers. It's a less clumsy
way for people to cope with binary numbers.
—The 650x microprocessor chip communicates with memory by sending an
address over its memory bus.
—The 650x has internal work areas called registers.
—The program counter tells us the address from which the processor will get
its next instruction.
—Three registers, called A, X, and Y, are used to hold and manipulate data.
They may be loaded from memory, and stored into memory.
—Addresses used in 650x instructions are "flipped:" the low byte comes first,
followed by the high byte.
—The machine language monitor gives us a new type of communications path
into the computer. Among other things, it allows us to inspect and change
memory in hexadecimal.

Detail: Program Execution


When we say G D33C to start up our program, the microprocessor goes
through the following steps:
FIRST CONCEPTS 21

1. It asks for the contents of $D33C; it receives SAD, which it recognizes as


the op code "load A." It realizes that it will need a two-byte address to
go with this instruction.

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.

Questions and Projects


Do you know that your computer has a part of memory called "screen
memory"? Whatever you put into that part of memory appears on the
screen. You'll find this described in BASIC texts as "screen POKE-ing."

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.

If you write a program to store information in the screen memory address,


the appropriate characters will appear on the screen. You might like to try
this. You can even "swap" characters around on the screen, if you wish.

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.

Here's another question. Suppose I asked you to write a program to move


the contents of five locations, $D3flD to $03fl4, in an "end-around"
fashion, so that the contents of $D3flD moved to $D3fil, $D3fll to
$D3flE, and so on, with the contents of $D3fl4 moved to $D3flD. At
first glance, we seem to have a problem: we don't have five data registers,
we have only three (A, X, and Y). Can you think of a way of doing the
job?
2
Controlling
Output
This chapter discusses:

• Calling machine language subroutines


• The PRINT subroutine
• Immediate addressing
• Calling machine language from BASIC
• Tiny assembler programs
• Indexed addressing
• Simple loops
• Disassembly

23
24 MACHINE LANGUAGE FOR COMMODORE MACHINES

Calling Machine Language Subroutines


In BASIC, a "package" of program statements called a subroutine may
be brought into action with a GO SUB command. The subroutine ends with
a RETURN statement, which causes the program to return to the calling
point, i.e., the statement immediately following GOSUB.

The same mechanism is available in machine language. A group of in


structions may be invoked with a jump subroutine (JSR) command. The
650x goes to the specified address and performs the instructions given
there until it encounters a return from subroutine (RTS) command, at
which time it resumes execution of instructions at the calling point: the
instruction immediately following JSR.

For example, if at address $D33C I code the instruction JSR $1E34,


the 650x will change its PC to $1E34 and start to take instructions from
that address. Execution will continue until the instruction RTS is encoun
tered. At this time, the microprocessor would switch back to the instruction
following the JSR, which in this case would be address $D33F (the JSR
instruction is three bytes long).

As in BASIC, subroutines may be "nested;" that is, one subroutine may


call another, and that subroutine may call yet another. We will deal with
subroutine mechanisms in more detail later. For the moment, we'll concern
ourselves with calling prewritten subroutines.

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:

Address Name What it does

$FFDE CHROUT Outputs an ASCII character


$FFE4 GETIN Gets an ASCII character
$FFE1 STOP Checks the RUN/STOP key

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.

C H R O U T—The Output Subroutine


The CHROUT subroutine at address $FFD2 may be used for all types
of output: to screen, to disk, to cassette tape, or to other devices. It's
similar to PRINT and PR IN T#, except that it sends only one character.
For the moment, we'll use CHROUT only for sending information to the
computer 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.

Registers: All data registers are preserved during a CHROUT call.


Upon return from the subroutine, ft, X, and Y will not have changed.

Status: Status flags may be changed. In the most recent Commodore


machines, the C (carry) flag indicates some type of problem with output.

To print a letter X on the screen, we would need to follow these steps:

1. Bring the ASCII letter X ($5fl) into the A register;

2. JSR to address $FFDE.


26 MACHINE LANGUAGE FOR COMMODORE MACHINES

Why Not POKE


It may seem that there's an easier way to make things appear on the
screen. We might POKE information directly to screen memory; in ma
chine language, we would call this a store rather than a POKE, of course.
The moment we change something in this memory area, the information
displayed on the screen will change. Screen memory is generally located
at the following addresses:

PET/CBM: $fiODD and up (decimal 327tfl)


Commodore 64 and 128: $D4DD and up (decimal 1DS4)
264/364 $DCDD and up (decimal 3D72)
VIC-20: $1EDD and up (decimal 7tflD)

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

First, the plan; we lay out the instructions

LD&

We're using a new symbol (#) to signal a special type of information. It


goes by a variety of names: pounds sign, sharp, hash mark, or numbers
sign. A more formal name for the symbol is octothorpe, meaning "eight
points." Whatever you call it, the symbol means "the following information
is not an address, it's a value." In other words, we don't want the computer
to go to address $4fl, we want it to load the A register with the value
$4 fl, which represents the ASCII letter H. This type of information access
is called immediate addressing. In other words, take the information im
mediately, don't go to memory for it.

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 machine language monitors contain extra commands to help us do


this type of mechanical translation. We'll use the assembler feature of
these monitors.

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.

These assemblers are often called nonsymbolic assemblers. This means


that whenever an address is needed, you must furnish that exact address.
You cannot type in a name such as CHROUT and expect the tiny assem
bler to know what address that represents; instead, you must type $ FFD 2.
C128 note: Remember to check Exercises for the Commodore 128, in
Appendix E, for the appropriate coding, and information on how the C128
assembler works.
28 MACHINE LANGUAGE FOR COMMODORE MACHINES

Load your monitor or monitor extension. Do any setup that may be needed.
Then type the following monitor command:

A D33C LDA #$4fl

We are asking the computer to assemble (A) at address $D33C (note


we don't use the $ here) the command LDA, Load A, the immediate value
of $4fl, which represents the ASCII letter H. When you press RETURN
after entering this line, the computer may do either of two things:

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:

A D33C LDA #$A&


A D33E

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:

A D33C LDA #$A&


AD33E JSR $FFD5
AD341 BRK
A D345

The computer is still waiting for another instruction. We have no more


instructions, so we press RETURN to signal that we're finished.

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.

Note that this saves us the trouble of remembering—or looking up—the


op codes for each instruction. And we don't need to keep track of how
long each instruction should be; the assembler does it for us.
CONTROLLING OUTPUT 29

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.

Checking: The Disassembler


When we changed our source code into object code, we called this process
of translation assembly, and we called a program that did the job an
assembler.

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.

If we ask the computer to disassemble the code starting at $ D3 3 C, it will


examine the code there and establish that the contents ($ AR) correspond
to an LD A immediate command. It will then print for our information LD A
#$A fl, which is much more readable than the original two bytes, AR A fl.

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.

An interesting feature of most disassembly listings is that the cursor is left


flashing on the last line of the disassembly rather than on the line below.
When you have a large program, this allows you to type the letter D
followed by RETURN and the next part of your program will immediately
be displayed. On the other hand, if you don't want to disassemble more
code, press the cursor down key and move to a "clean" line before typing
your next instruction.
30 MACHINE LANGUAGE FOR COMMODORE MACHINES

A disassembly is a good way to check for errors. If you find an error in


the listing, you may correct that line by re-assembling it, using the ft
command once again. Minor errors may be corrected directly on the left-
hand side of the disassembly listing. In other words, suppose that you had
incorrectly coded LDft #$5fi during the assembly phase; when you per
form the disassembly, this line will show as

. , D33C AH 5fl LDA#$5fi

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.

Running the Program


If necessary, move the cursor down to an empty line. Type the command
G D 3 3 C and the program will run. Again, it doesn't take long; the break
back to the MLM seems instantaneous. Where's the letter H that we were
supposed to print? It's hard to see, but it's there. Look at your G D33C
command and you'll see it.

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.

Linking with BASIC


So far we have started up our programs with a G (go) command from
the MLM, and we have terminated our programs with a BRK command
that returns us to the monitor. That's not a convenient way to run a program;
most users would prefer to say RUN out of BASIC and have the computer
do everything.

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

(BASIC) SYS—Go to a machine language subroutine at the stated address;


(Machine language) RTS—Return to whoever called this subroutine.
CONTROLLING OUTPUT 31

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

A D33C LDA #$4fl


A D33E JSR $FFD2
A 0341 RTS
Now return to BASIC (using the X command). The computer will say
READY; you may now call your program with a SYS command.

Address $D33Cisfl2flin decimal. Thus, we type SYS fl5fl. When we


press RETURN, the letter H will be printed.

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.

Project for enthusiasts: Again, change the machine language program


to say HI. Use your imagination. What else would you like the computer
to say? Would you like to use colors or reverse font?

We've achieved an important new plateau: BASIC and machine language


working together, it's easier on the user, who doesn't have to learn spe
cialized monitor commands. It's easier on the programmer, too, since
things that are easy to do in BASIC can be written in that language; things
that are clumsy or slow in BASIC can be written in machine language. We
can get the best of both worlds.

Let's distinguish our three different types of subroutine calls:


GO SUB—calls a BASIC subroutine from a BASIC program.
SYS—calls a machine language subroutine from a BASIC program.
JSR—calls a machine language subroutine from machine language.
32 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

Let's write a program to print the word HELLO followed by a RETURN.

C128 note: Remember to check Exercises for the Commodore 128, in


Appendix E, for the appropriate coding.

We must store the word HELLO somewhere in memory. It doesn't matter


where, provided it doesn't conflict with anything else. I'll arbitrarily choose
address $D34Ato$D34F. We'll put it there in a moment. Remember
that the characters that make up the word HELLO (plus the RETURN)
are not program instructions; they are simple data. We must put them in
place with a memory change—we must not try to assemble them.

We will need to count the characters as we send them. We wish to send


six characters, so a count of six is our limit. Let's use the X register to
keep track of the count. First, we must set X to zero:

A D33C LDX #$DD

Note that we use the # symbol to denote an immediate value: we want


to load X with the value zero, not something from address D. Now, we'll
do something new. I want to take a character to be printed from address
$D34 A. But wait, that's only the first time around. When we come back
to this point in the loop, I want to take a character from $ D 3 A B, and then
from $03 AC, and so on.

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

we go around the loop, the letters, L, L, 0, and RETURN will be brought


in as needed.

As we enter the LD A instruction, we don't type the plus sign. Instead, we


signal indexing with a comma: LDA$D34A,X.We may use either X or
Y for indexing: they are sometimes called index registers. In this case, of
course, we use X. So we code

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.

Let's show the whole code, completing it with RTS:


A D33C LDX #$DD
A D33E LDA $D34A,X
A D341 JSR $FFD2
A D344 INX
A D34S CPX #$Dt
A D347 BNE $D33E
A D3^q RTS
34 MACHINE LANGUAGE FOR COMMODORE MACHINES

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

By a lucky coincidence, this data fits exactly behind our program.

Everything should be ready now. Disassemble the program at $D33C


and check it. You may note that the data at $Q3AA doesn't disassemble
too well, but that's to be expected; these bytes are not instructions and
cannot be decoded.

When all looks well, return to BASIC (with . X) and try SYS flEfl. The
computer should say HELLO.

Once again, set up a BASIC loop program:

1DD FOR J = 1TO3


11D SYS flEfl
1ED NEXT J

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.

We note that the program extends from $D33Cto$D34F, including the


message (HELLO) at the end. The decimal equivalents to these ad
dresses are fl E fl to fl A 7. C128 note: Appendix E, in the section Exercises
CONTROLLING OUTPUT 35

for the Commodore 128, will give you the correct addresses and values
for doing this on the C128. Enter the following BASIC line:

FOR J = flEfl TO fl47:PRINT PEEK(J) ; :NEXT J

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.

We can now reconstruct our machine language program, placing it back


into memory, with a simple BASIC POKE program:

flD FOR J = fiEfl TO fl47: READ X:POKE J,X:NEXT J

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

50 DATA lt3,D, Iflq,74,3,35,510,555,535,224, t


tO DATA 50fl,5^5,qfc,75,tq,7t,7t,7q,13
flO FOR J=fl5fl TO fl47 : READ X: POKE J,X:NEXT J
100 FOR J= 1 TO 3
110 SYS fl5fl
150 NEXT J

This method of saving a machine language program is clean and trouble


free, but it becomes awkward where long programs are involved. More
advanced methods will be discussed in Chapter 6.
36 MACHINE LANGUAGE FOR COMMODORE MACHINES

Things You Have Learned


—Subroutines can be called from machine language using the JSR command.
There are several useful kernal subroutines permanently available.
—A BASIC program may call a machine language program as a subroutine:
the BASIC command is SYS. The machine language subroutine returns to
the calling point with an RTS (return from subroutine) instruction.
—The CHROUT subroutine at address $FFD2 allows output of a character,
usually to the screen. In addition to printable characters, special cursor- and
color-control characters may be sent.
—Most machine language monitors have a small assembler to help program
preparation, and a disassembler to assist in program checking.
—Immediate mode is signaled by use of the # symbol. The computer is asked
to take the value given, instead of going to a specified address for its data.
—X and Y are called index registers. We may add the contents of X or Y to a
specified address, to create an effective address that changes as the program
runs. This addition is called indexing.
—X and Y also have special instructions that increase or decrease the selected
register by one. These are called increment and decrement instructions, and
are coded INX, INY, DEX, and DEY.

Questions and Projects


Look through the table of ASCII characters in Appendix D. Note that hex
R3 is "clear screen." Write a program to clear the screen and print "HO
H0!».

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:

• Flags that hold status information


• Testable flags: Z, C, N, and V
• Signed numbers
• The status register
• First concepts of interrupt
• Logical operators: OR, AND, EOR
• The GETIN subroutine for input
• The STOP subroutine

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.

In other words, some instructions leave a "trail" of status information; other


instructions can check this information. The status information is called
"flags." There are four flags that may be tested: Z, C, N, and V. They are
discussed below.

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.

A Brief Diversion: Signed Numbers


How can a location—which is usually thought to contain a decimal value
from D to E 5 5—contain a negative number? It's up to the programmer
to decide whether a memory value is unsigned, having a value range from
D to 555, or signed, having a value range from - lEfl to +157. There
are still a total of E5t possibilities. The computer's memory simply holds
bits, while the programmer decides how the bits are to be used in a specific
case.

Mathematically, it's described this way: signed numbers, if desired, are


held in two's-complement form. We can hold -1 as hex FF, and - 5
as hex FE, all the way down to -1E fl as hex fl D. You may have noticed
that in all the examples, the high bit is set for these negative numbers.

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.

Let's discuss a decimal analogy. Suppose you have a cassette recorder


with a counter device attached, and the counter reads 0025. If you rewind
the unit a distance of 30 units, you would not be surprised to see a value
of 9995 on the counter and would understand that it meant a position of
-5. If you had a car with 1,500 miles on the odometer, and "rolled back"
44 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

A Brief Diversion: Overflow


The term overflow means "the result is too big to fit." For example, if I
add EDD to EDO, the total is 4QQ ... but this won't fit in a single byte.
If we have only a single byte to store the result, we say that the addition
has encountered overflow, and we can't produce a meaningful answer.

If we are using unsigned numbers, the C flag tells us about overflow. If


we are using signed numbers, V tells the story. We'll take this up again
in the next chapter.
FLAGS, LOGIC, AND INPUT 45

Flag Summary
A brief table may help review the four testable flags.

Flag Brief Activity Branch Taken If:


Name Meaning Level Set Not-Set
Z Zero, equal Busy BEQ BNE
C Carry, greater/equal Quiet BCS BCC
N Negative, high-bit Busy BMI BPL
V Signed arithmetic overflow Quiet BVS BVC

The Status Register


The preceding flags—and three others—may be viewed within the status
register (SR). You may recall that the machine language monitor gives
an SR display. If you know how to read it, you can see the condition of
all 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

Taking the bits one at a time, starting at the high bit:


N—the N flag, as above
V—the V flag, as above.

Bit 5—unused. You'll often find that this bit is "on."


B—"Break" indicator. When an interrupt occurs, this signals whether or not the
interrupt was caused by a BRK instruction.
D—Decimal mode indicator. This changes the manner in which the add and
subtract instructions operate. In Commodore machines, this flag will always be
off. Don't turn it on unless you know exactly what you're doing. This flag may
be turned on with the SED (set decimal) instruction, and turned off with the
CLD (clear decimal) instruction.

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.

C—the C flag, as above.


46 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

When we see a value displayed in the SR, or status register, we may


examine it to determine the condition of the flags, especially the testable
flags Z, C, N, and V. For example, if we see an SE value of $B1, we
translate to binary *1D11DDD1 and know that the N flag is on, the V
flag is off, the Z flag is off, and the C flag is on.
You may change these flags by typing over the displayed value in the
machine language monitor. Be careful you don't accidentally set the D or
I flags.

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 will use the appropriate compare instruction depending on the register


involved; CMP for the A register, CPX for the X register, and CPY for the
Y register. Following the comparison, we may use any of the following
branch tests:

BEQ—branches if the two bytes are equal.


BNE—branches if the two bytes are not equal.
BCS—branches if the value in the register is greater than or equal to the other
value.
BCC—branches if the value in the register is less than the other value.

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.

We have seen JSR, which allows us to call a subroutine of prewritten


instructions. We've used RTS, which says, "Go back to the calling point,"
even if the calling point is a BASIC program. And we've almost abandoned
the BRK instruction, which stops the program and goes to the machine
language monitor. BRK will be useful in checking out programs. Specifi
cally, we can stop a program at any time by inserting a BRK instruction,
allowing us to see whether the program is behaving correctly and whether
it has done the things we planned.

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

Mathematicians describe these operations as commutative. For example,


a value of $3 A "AND11 $5? gives exactly the same result as $57
"AND" $3A. The order doesn't matter. But we often use these func
tions—and think of them—in a particular order. It's the same as with
addition, where we think of a "total" to which is added an "amount" to
make a "new total." With the logical operators we often think of a "value,"
which we manipulate with a "mask" to make a "modified value."

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—turns bits off.

ORA—turns bits on.

EOR—flips bits over.

AND—Logical AND to A
For each bit in the A register, AND performs the following action:

Original A Bit Mask Resulting A Bit


D 0 0
1 D D
DID
1 1 1

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.

Example: Turn off bits 4, 5, and t in the following value: $C7

Original value: 11DDD111


Mask: AND 1DDD1111 (hex flF)

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.

Example: Turn on bits 4,5, and t in the following value: $C7


Original value: 11DDD111
Mask: ORA D111DDDD (hex 7D)

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.

Example: Invert bits 4, 5, and t in the following value: $C7


Original value: 11DDD111
Mask: EOR D111DDDD (hex 7D)

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 Logical Operations?


We use these three commands—AND, OR a, and EOR—to change or
control individual bits within a byte of information. The commands are
unusual in that each bit may be manipulated independently of the others.

We don't seem to be working with numbers when we use these commands.


Rather, we're working with each individual bit, turning it on or off as we
wish.

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.

Why would we want to flip bits over? Many "oscillating" effects—screen


flashing or musical notes—can be accomplished this way.

Finally, the logical operators can be useful in code translation. For ex


ample, here are the values for ASCII 5 and binary 5:

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.

Input: The GE TIN Subroutine


We have seen how we can use CHROUTat$FFDEto produce output
to the screen. Now we'll look at the input side—how to use the GETIN
subroutine at $FFE4 to get characters from the keyboard buffer.
FLAGS, LOGIC, AND INPUT 57

You may be familiar with the GET statement in BASIC. If so, you'll find
the same characteristics in GET IN:

• Input is taken from the keyboard buffer, not the screen.

• If a key is held down, it will still be detected once only.


• The subroutine returns immediately.

• If no key is found, a binary zero is returned in A.


• If a key is found, its ASCII value will be in A.

• Special keys, such as RETURN, RVS, or color codes, will be detected.

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.

Registers: The A register will of course always be affected. X and Y


are likely to be changed; do not have data in these when calling
GETIN.
Status: Status flags may be changed. In most recent Commodore
machines, the C (carry) flag indicates some type of problem with input.

If we want keyboard input to appear on the screen, we should follow a


call to GETIN, $FFE4, with a call to CHRODT, $FFDE, so that the
received character is printed.

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

A call to FFE1 should be followed by a BEQ to a program exit so that


the program will terminate when RUN/STOP is pressed.

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.

Registers: A will be affected. X will be affected only if the RON/STOP


key is being pressed.

Status: Z signals whether RON/STOP is being pressed.

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.

Coding sheets ready? Here we go.

A D33C JSR $FFE1

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 ...

A 033F BEQ $0351


A D341 JSR $FFE4

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

A U3AA CMP #$3D


A 034b BCC $033C

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:

A034B CMP #$3A


A 034A BCS $033C

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:

A 034C JSR $FFDE

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:

A 034F AND #$0F


A 0351 RTS

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).

By a fortunate stroke of luck, we happen to have guessed the right address


for the BE Q at address $ D 3 3 F. But if we hadn't, you know how to change
it, don't you?

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.

Things You Have Learned


—Flags are used to link instructions together. This might be an activity such
as load or compare, followed by a test such as branch on a given condition.
—Some instructions affect one or more flags, and some do not affect flags.
Thus, an instruction that sets a flag might not be followed immediately with
the instruction that tests or uses that flag.
—There are four testable flags: Z (zero, or equals); C (carry, or greater/equal);
N (negative, or high bit); and V (signed arithmetic overflow). The flags are
checked by means of "branch" instructions such as BEQ (branch equal) or
BNE (branch not equal).
—Flags are stored in the status register, sometimes called the processor status
word. The S R contains the four testable flags, plus three other flags: B (break
indicator); D (decimal mode for add/subtract); and I (interrupt lockout). The
hexadecimal value in SR can be changed to binary and used to determine
the exact condition of all flags.
—Usually, the processor is interrupted sixty times a second to do special high-
priority jobs. Everything, including the status register flags, is carefully pre
served so that the main program can continue as though nothing had
happened.
—A number stored in memory can be considered as signed if we decide to
handle it that way. The value of a signed number is held in two's-complement
form. The high bit of the number is zero if the number is positive, one if the
FLAGS, LOGIC, AND INPUT 55

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.

Questions and Projects


Write extra coding to allow both numeric and alphabetic characters, but
nothing else.

Write a program to accept only alphabetic characters. As each ASCII


character is received, turn on its high bit with ORA #$flD and then print
it. How has the character been changed?

Write a program to accept only numeric digits. As each ASCII character


is received, turn off its lowest bit with AND #$FE and then print it. What
happens to the numbers? Can you see why?
4
Numbers,
Arithmetic,
and
Subroutines
This chapter discusses:

• Numbers: signed and unsigned


• Big numbers: multiple bytes
• Arithmetic: add and subtract
• Rotate and shift instructions
• Multiplication
• Home grown subroutines

57
58 MACHINE LANGUAGE FOR COMMODORE MACHINES

Numbers: Signed and Unsigned


We have looked briefly at the question of signed versus unsigned numbers.
The most important concept is that you, the programmer, choose whether
or not a number is to be considered a signed number (for a single byte,
in the decimal range -lEfl to +157) or an unsigned integer (single-
byte range D to 255).

It makes no difference to the computer. If you consider a number signed,


you may wish to test the sign using the N flag. If not, you won't do such
a test.

Big Numbers: Multiple Bytes


You may use more than one byte to hold a number. Again, it's your
decision. If you think the numbers may go up to a million, you might allocate
three bytes (or more or fewer). If you are doing arithmetic on multi-byte
numbers, the computer will help you by signaling in the carry flag that
there's something to be carried across from a lower byte to a higher one.
But it's up to you to write the code to handle the extra bytes.

You may size numbers by using the following table:


Unsigned: Signed:
1 byte 0 to 255 -128 to +127
2 bytes 0 to 65,535 -32768 to +32767
3 bytes 0 to 16,777,215 - 8,388,608 to + 8,388,607
4 bytes to over 4 billion -2 billion to +2 billion
It's possible to work with binary fractions, but that is beyond the scope of
this book. Many applications "scale" numbers, so that dollar-and-cents
amounts are held as integer quantities of pennies. Thus, two bytes un
signed would hold values up to $b55.35, and three bytes up to
$lk7,772.15.

When signed numbers are held in multiple bytes, the sign is the highest
bit of the highest byte only.

We will concentrate on single-byte arithmetic principles here, touching on


multiple-byte numbers as a generalization of the same ideas.

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 1: We start at the right-hand column (the low-order byte).


Rule 2: We add the two values, plus any carry from the previous column. A new
carry may be generated; it can never be greater than one. (ADC includes any
carry from a previous activity, and may generate a new carry bit, which is either
D or 1.)

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.)

HIGH BYTE LOW BYTE

START:
OO1O1O11 10111001 N0CAR«Y
4- Q0001010 11100101/
10011110

CARRY

/
00110110'
Figure 4.1

How do we translate these rules into machine language addition?

1. Before we start an addition sequence, clear the carry with CLC.


2. If the numbers are more than one byte in size, start at the low byte and work
up to the high ones. Addition will take place in the A register only; you may
add the contents of an address or an immediate value. The carry flag will
take care of any carries.

3. When the addition sequence is complete, check for overflow:


a) if the numbers are unsigned, a set C flag indicates overflow;
b) if the numbers are signed, a set V flag indicates overflow.

Thus, to add two unsigned numbers located at addresses $D3flO and


$D3fil and to place the result at $D3flE, we might code
60 MACHINE LANGUAGE FOR COMMODORE MACHINES

CLC
LDA$D3flD
ADC $D3fll
STA $D3fi2

We might also BCS to an error routine, if desired.

To add a two-byte number located at $D3AD (low) and $D3A1 (high)


to another two-byte number located at $D3BD (low) and $D3B1 (high),
placing the result at $D3CD/1, we might code

CLC
LDA $D3AD
ADC $D3BD
STA $D3CD
LDA $D3A1
ADC $D3B1
STA $D3C1

Again, we might BCS to an overflow error routine.

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.

Thus, to perform a subtraction, we follow these rules:


1. Before we start a subtraction sequence, set the carry with SEC.
2. If the numbers are more than one byte in size, start at the low byte and^work
up to the high ones. Subtraction will take place in the A register only; you
may subtract the contents of an address or an immediate value. The C flag
will take care of any "borrows."
3. When the subtraction sequence is complete, check for overflow:
a) if the numbers are unsigned, a clear C flag indicates overflow;
NUMBERS, ARITHMETIC, AND SUBROUTINES 61

b) if the numbers are signed, a set V flag indicates overflow.

Thus, to subtract two unsigned numbers located at addresses $ D 3 fl D


and $D3fll and to place the result at $03fl5, we might code

SEC
LDfi $03flD
SBC $D3fil
STft $D3flE

A BCC could go to an error routine.

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.

The easiest way to go about such a comparison is to subtract one number


from the other. You need not keep the result; all you care about is the
carry flag when the subtraction is complete. If the C flag is set, the first
number (the one you are subtracting from) is greater than or equal to the
second number. Why? Because carry set indicated that the unsigned
subtraction was legal; we have subtracted the two numbers and have
obtained a positive (unsigned) resuit. On the other hand, if the C flag ends
up clear, this would mean that the first number is less than the second.
The subtraction couldn't take place correctly since the result—a negative
number—can't be represented in unsigned arithmetic.

Left Shift: Multiplication by Two


If we write the decimal numbers 1DD and 5DD in binary, we see an
interesting pattern:

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.

The command to multiply a byte by two is ASL (arithmetic shift left). A


zero bit is pushed into the low (or "right") side of the byte; all bits move
62 MACHINE LANGUAGE FOR COMMODORE MACHINES

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

(1 HIGH ORDER BYTE

TO MULTIPLY A THREE-BYTE NUMBER BY TWO, WE


SHIFT THE LOW ORDER BYTE WITH ASL; THEN WE USE
ROL TO ALLOW THE C FLAG TO "LINK" FROM ONE
BYTE TO THE NEXT.

Figure 4.4

We won't deal with a generalized multiplication routine here, but a couple


of specific examples can be shown.

How can we multiply by four? Multiply by two, twice. How can we multiply
by eight? Multiply by two, three times.

Here's an important one. We often want to multiply by ten. For example,


if a decimal number is being typed in at the keyboard, the number will
arrive one digit at a time. The user might type E17, for example. The
program must then input the two and put it away; when the one arrives,
the two must be multiplied by ten, giving twenty, and the one added; when
the seven is typed, the twenty-one must be multiplied by ten before the
seven is added. Result: 217 in binary. But we must first know how to
multiply by ten.

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.

Right Shift and Rotate: Dividing by Two


If we can multiply by two by shifting (and rotating) left, we can divide by
two by moving the bits the other way. If we have a multibyte number, we
must start at the high end.

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~

IN A ROR, THE CARRY MOVES INTO THE HIGH BIT AND


ALL BITS MOVE RIGHT ONE POSITION; THE LOWEST
BIT BECOMES THE NEW CARRY.

0 LSR

ROR

C~T —- —- —» \—\ ROR

TO DIVIDE A THREE-BYTE NUMBER BY TWO, WE SHIFT


THE HIGH-ORDER BYTE WITH LSR; THEN WE USE ROR
TO ALLOW THE C FLAG TO "LINK" FROM BYTE TO
BYTE.

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.

Comments on Shift and Rotate


As you might expect of arithmetic instructions, the shift and rotate instruc
tions normally operate in the ft register. But there's an extra bonus: these
instructions also can operate directly on memory. In other words, the
computer can go to any address in memory and shift the bits at that address
directly, without loading the data into a register.

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

LSR a so as to distinguish from LSR $1234, where the contents of


memory is being shifted.

When a rotate or shift is performed directly on a memory location, the Z,


N, and C flags are affected according to the contents of memory. Z will
be set if the contents of the location ends up as zero; N if the high bit is
set; and C performs its standard role of catching the leftover bit.

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.

A 033C JSR $FFE1


A 033F BEQ $0351
A 0341 JSR $FFE4
A D3AA CMP #$30
A 034b BCC $033C
A D3A& CMP #$3A
A 034A BCS $033C
A 03AC JSR $FFD5
A 03AF AND #$0F
A 0351 RTS
66 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

C128 note: Remember to check Appendix E, Exercises for the Commo


dore 128, for the appropriate coding.

Here we go. We must start our coding at address $D352 so as not to


disturb our subroutine. We'll need to give SYS fl5D to make this one go.

& D352 JSR $D33C

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:

A D355 STa $D3CD


a D35fl LDa #$2B
a D35a JSR $FFD2
aD35D JSR $D33C

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.

We could place the value into memory—perhaps $D3C1 would do—but


there's another way. We don't seem to be using X or Y for anything at
NUMBERS, ARITHMETIC, AND SUBROUTINES 67

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.

Assuming the total is in the range D to q, we can convert it directly to a


single ASCII digit with an OR A operation. (If it's greater than nine, you're
cheating and the answer won't make sense.)

A D3tB ORA #$3D


A D3tD JSR $FFD5

Are you basically a neat person? Then you'll want to print a RETURN to
start a new line:

ft D3?D LDft #$DD


ft D375 JSR $FFD5
ft D375 RTS

Check it with a disassembly. If you disassemble starting with the subrou


tine, you'll need more than one screen full of instructions to see it all. No
problem. When the cursor flashes at the bottom of the screen, press the
letter D and RETURN and you'll see a continuation of the listing.
68 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

Things You Have Learned


—We may decide to use a number as a signed value; in this case, the high bit
of the number will be D if the number is positive and 1 if the number is
negative. It's up to us. As far as the computer is concerned, it's just bits in
either case.
—When a number might have a value that won't fit into an eight-bit byte, we
may use more than one byte to hold the value. We have already done this
to hold addresses in two bytes: there's a high byte to hold the high part of
the value and a low byte to hold the low part.
—We may add two numbers together using the ADC instruction with the A
register; we should always clear the carry flag before starting an addition.
The carry flag will take care of multibyte numbers for us, providing we re
member to start the addition at the low end.
—We may subtract two numbers using the SBC instruction with the A register;
we should always set the carry flag before starting a subtraction. The carry—
which is sometimes called an inverted borrow—will take care of multibyte
numbers for us, providing we remember to start the subtraction at the low
end.
—For unsigned numbers, the carry should end up as it started (clear for addition,
set for subtraction); otherwise we have overflow in the result. For signed
numbers, the carry doesn't matter; the V flag will be set if we have overflow.
—We may multiply a byte by two with the ASL (arithmetic shift left) instruction.
If we have a multiple-byte number, we may carry the multiplication through
to other bytes by using the ROL (rotate left) instruction, starting at the low
byte of the number.
—We may divide a byte by two with the LSR (logical shift right) instruction. If
we have a multiple-byte number, we may carry the division through to other
NUMBERS, ARITHMETIC, AND SUBROUTINES 69

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.

—As we might have expected, we may write subroutines in machine language


and then call them from machine language. It's a good way to organize your
code.

Questions and Projects


Write a program to subtract two single-digit numbers, similar to the one
in the above exercise. You may continue to use the subroutine from the
previous chapter.

Write a program to input a single-digit number. If the number is less than


five, double it and print the result. If the number is five or over, divide it
by two (discarding any remainder) and print the result. Try to produce a
neat output.

Write a program to input a single-digit number. Print the word ODD or


EVEN behind the number, depending on whether it is odd or even. Use
the LSR instruction followed byaBCCorBCS test to check for odd or
even.

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:

• Non-addresses: implied, immediate, register


• Absolute and zero-page
• Indexing

• The relative address for branches


• Indirect addressing
• Indirect, indexed

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:

1. No memory address: implied, accumulator.


2. No address, but a value supplied: immediate.
3. An address designating a single memory location: absolute; zero-page.
4. An indexed address designating a range of E5fc locations: absolute,x; ab
solute^; zero-page,x; zero-page,y.

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.

No Address: Implied Mode


Instructions such as INX (increment X), BRK (break), and TRY (transfer
R to Y) need no address; they make no memory reference and are com
plete in themselves. Such instructions occupy one byte of memory.

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.

Perhaps the word "implied" is usecj in this manner: an instruction such as


INX implies the use of the address register; and an instruction such as
BRK implies the address of the machine language monitor. If so, there's
an instruction that still defies this definition: NOP.

The Do-Nothing Instruction: NOP


NOP (no operation) is an instruction that does nothing. It affects no data
registers or flags. When a NOP instruction is given, nothing happens and
the processor continues to the next instruction. It seems inappropriate to
ADDRESS MODES 73

me that we say that NOP has an implied address. It doesn't do anything;


it doesn't have an address at all. On the other hand, I suppose that logicians
might say, "Yes, but it does nothing to the X register."

The NOP instruction, whose op code is $EA, is surprisingly useful. It's


not simply that if you're a contract programmer getting paid by the byte
you might be tempted to put a large number of NOP instructions into your
program. NOP can serve two important program testing functions: taking
out unwanted instructions, or leaving space for extra instructions.

It's not as easy to change a machine language program as it is to change


a BASIC program. As you have seen, the instructions are placed in specific
locations. If we wish to eliminate an instruction, we must either move all
the following instructions down or fill in the space with NOP instructions.
If we move the instructions, we may need to correct some of the addresses.

Examine the following code:

D35D LDA#$DD
D35E STA $1Z34
D355 ORA

If we decide to eliminate the instruction atD352(STA$ia34), we must


remove all three bytes. So we place code $E A in locations D352,D353,
and D354.

Suppose we are testing a moderately large program. Most programs will


break into distinct "modules," each of which does a specific job. One
module might clear a portion of memory to zero, another might do a
calculation, and so on. When we are checking out this program, it might
be wise to look at each module as it runs.

In this case, we might deliberately code a BRK (break) command between


each program module. The program will start to run, and then it will break
to the machine language monitor. Within the monitor, we can examine
memory to ensure that this module has done the job as we planned it.
When we are satisfied, we can start the next module using the . G com
mand. In this way, we can have tight testing control over our program.

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.

No Address: Accumulator Mode


We have observed that the shift and rotate instructions, ASL/ ROL,
LS R, and ROR, allow data manipulation in either the A register of directly
in memory. When we want to use the A register, or accumulator, you
should note this fact as you code your program. For example, you would
write ASL A or sometimes just ASL.

Where accumulator mode addressing is used, it has the same character


istics as implied addressing: the whole instruction fits into one byte.

Where the shift/rotate instruction refers to a memory location, an address


will of course be needed. These address modes will be described later.

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.

When an instruction modifies memory, the address mode is neither implied


nor accumulator. Memory reference addressing will be discussed later.

Not Quite an Address: Immediate Mode


Coding such as LDA #$3A does not reference a memory address. In
stead, it designates a specific value (in this case, $34). An instruction
with immediate addressing takes up two bytes: one for the op code and
the second for the immediate value.

We have used immediate addressing several times. It has a "natural" feel,


and it's fast and convenient. There is one potential pitfall: immediate ad
dressing is so easy to use that it may be abused. Each time you code an
immediate address, ask yourself, "Could this value ever change?" By
writing a value into a program, rather than a variable, you may be freezing
that value forever.

An example: a program is written for a VIC-20, which has EE columns on


the screen. At various places in the program, values are compared to 25
(hex It), and 55 is added or subtracted to various screen addresses. In
ADDRESS MODES 75

each case, immediate mode addressing is used to provide the value of


25. Some time later, the programmer decides to convert to the Com
modore 64, which has A 0 columns on the screen. The programmer must
change each immediate mode reference from 52 to 4D (hex 5fl).

If the value EB had been stored in a memory location so as to be used


as a variable, all this recoding would not be needed. The moral is clear:
excessive use of immediate mode can call for extra programming work at
a later time.

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.

A Single Address: Absolute Mode


An instruction might specify any address within memory—from $ D D D D
to $FFFF—and handle information from that address. Giving the full
address is called absolute addressing; if you like, you can deal with in
formation absolutely anywhere in memory.

I MEMORY 1
/
Figure 5.1 Absolute Mode Specifies One Address Anywhere Within Memory.

We have used absolute addresses several times. When we exchanged


the contents of memory locations $ D 3 fl D and $ D 3 fl 1, we named these
addresses as we used them. When we stored a value from the keyboard,
we named location $D3CD. We have also used absolute addresses for
program control: subroutines at $FFD5 and $D33C were called up sim
ply by giving the address.

The JSR (jump subroutine) instruction calls up a subroutine anywhere in


memory by using absolute addressing. There is also a JMP (jump) in
struction, which can transfer program execution to any location in memory;
it's similar to the BASIC GOTO statement. JMP can use absolute ad
dressing—it can go anywhere.

There's a limitation to absolute addressing, however. Once you have writ


ten the instruction, you can go only to the address stated. You cannot
reach a range of locations; only one.

One-location addressing can be good for any of several jobs. On the PET/
76 MACHINE LANGUAGE FOR COMMODORE MACHINES

CBM, we might want to switch between text and graphics modes by ma


nipulating address 5 q A fc fl (hexadecimal E fl A C). On the VIC-20, we might
like to set the volume level of the sound generator by placing a value into
location 3bfl?fl (hex HDDE). On a Commodore 64, the screen's back
ground color can be changed by manipulating address 5 3 E fl 1 (hex D D E1).
In each case, it's one specific address that we want; absolute addressing
will do the job for us. And we will also use absolute addressing to reference
the various RAM locations that we have picked for our own program "var
iables."

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.

$0D $FF $1DO

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).

Zero-page addressing, like absolute addressing, references one location


ADDRESS MODES 77

only. It's good for a specific value; but for a range of values we need
something more.

A Range of 2 5 b Addresses: Absolute,


Indexed Mode
Indexing has already been used in Chapter 2. We give an absolute ad
dress, and then indicate that the contents of X or Y should be added to
this address to give an effective address.

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.

An instruction using absolute, indexed addressing can reach up to E5t


locations. Registers X and Y may hold values from 0 to E55, so that the
effective address may range from the address given to E55 locations
higher. Indexing always increases the address; there is no such thing as
a negative index when used with an absolute address. If the address given
is above $FFDD, a high value in the index may cause the address to
"wrap around" and generate an effective address in the region of $ □ D D D;
otherwise, the effective address is never lower than the instruction ad
dress.

We've seen the use of indexing. An instruction can reference a certain


address, then, as the program loops or as the need for information changes,
the same instruction can reference the contents of a different address.
The maximum range of E5b locations is an important limitation.
78 MACHINE LANGUAGE FOR COMMODORE MACHINES

The "reach" of an absolute, indexed instruction allows it to handle infor


mation in buffers (such as the input buffer, keyboard buffer, cassette buffer);
tables (such as the active file table); and short messages (such as HELLO
or error messages). It's not big enough, however, to reach all parts of
screen memory, all parts of a BASIC program, or all of RAM. For that,
we'll use indirect, indexed addressing, which will be described later.

All of Zero Page: Zero-Page, Indexed


Zero-page, indexed addressing seems at first glance to be similar to the
absolute, indexed mode. The address given (this time in zero-page) has
the contents of the selected index added to it. But there's a difference: in
this case, the effective address can never leave zero page.

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.

On Commodore machines, zero page is fairly well occupied. There is


limited opportunity to use zero-page, indexed addressing.
ADDRESS MODES 79

Branching: Relative Address Mode


We have written several branch instructions already; the assembler al
lowed us to enter the actual addresses to which we want to branch. The
assembler translates it to a different form—the relative address.

Figure 5.5

Relative address means, "branch forward or backwards a certain number


of bytes from this point." The relative address is one byte, making the
whole instruction two bytes long. Its value is taken as a signed number.

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.

We can calculate a branch by performing hexadecimal subtraction; the


"target" address is subtracted from the PC address. If we have a branch
at $ D 3 A1 that should go to $ D3 3 C, we would work as follows: $ D3 3 C
(the target) minus $D343 (the location following the branch instruction)
would give a result of IFR, or minus 7. This is tedious to do, and often
results in mistakes; such mistakes in calculating a branch address are
often fatal to the program run. We are much better off using an assembler
to work out the arithmetic for us.

The longest branches are: $7 F, or 127 locations ahead; and $flD, or


15 fl locations back. This poses no difficulties with short programs, such
as the ones we are writing here. But in larger programs, the branch may
not be able to reach far enough. The usual solution to this is to place a
JMP Gump) instruction nearby, which is capable of going anywhere in
memory; JMP uses absolute addressing. The appropriate branch instruc
tion will go to the JMP, which in turn will take the program to the desired
location.

Advocates of programming style make the following argument. All pro


grams should be written into neat small modules. Logic blocks should be
80 MACHINE LANGUAGE FOR COMMODORE MACHINES

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 interesting aspect of relative addressing is that code containing branches


is easy to relocate. A piece of code containing a branch to six locations
ahead will work perfectly if the whole code is moved to a different location.
This is not true of jumps and subroutine calls, or any code using absolute
addressing—if the location changes, the address must be changed.

The ROM Link—Jumps in Indirect Mode


We have mentioned the JMP instruction that will take the program to any
specified address. JMP has another address mode: indirect addressing.

Indirect addressing is signaled by the use of parentheses around the


address. It works this way. An address is supplied, but it's not the one we
will eventually use. We take this address, and at the location it specifies,
we'll find the effective address, or the indirect address. The indirect ad
dress is two bytes long, of course, and is stored in the usual 650x manner
of low byte first.

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

The JMP indirect has a somewhat specialized use. Normally, if we want


to transfer control to some location, we just JMP there; no need for the
indirect step. But there's one quite important case where indirect jumps
serve an important function.

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.

Data From Anywhere: Indirect, Indexed


The problems with indexed addressing have been noted: the reach of only
S5b bytes limits the data capability of this method.

Indirect addressing seems to offer a total solution. We can write an in


struction that points at an indirect address. Since we can change the
indirect address at will, or add to or subtract from it, we can cause our
instruction to deal with data anywhere in memory.

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.

Indirect addressing plus indexing proves to be an ideal combination for


the manipulation of data. Almost all data breaks up into logical chunks of
some sort: records, table entries, screen lines, words, and so on. Here's
the technique. We position the indirect address at the start of a given
logical data chunk, and use the Y register to scan through the information.
When we're ready to move to the next item, we move the indirect address
along, and repeat the same scanning of the Y register through the new
data.

00 FF

INDIRECT,
INDEXED

Figure 5.7

One may think of it as a fishing analogy: We anchor the boat in a certain


spot (fix the indirect address) and then use the fishing line (the Y register)
to reach the data we need. When we're ready for the next item, we pull
up the anchor and move along to a new place.

-DATA IN MEMORY-

NAME, ETC. NAME, ETC. NAME, ETC.

Figure 5.8

We'll be working through an elaborate example that uses indirect, indexed


addressing to manipulate the computer screen. First, a brief diversion.
ADDRESS MODES 83

A Rarity: Indexed, Indirect


There is another addressing mode that is little used in Commodore com
puters: indexed, indirect. It uses the X register rather than the Y, and is
coded as in the following example: LD A ($CD, X). In this case, indexing
takes place first. The contents of X are added to the indirect address (in
this case, $CD) to make an effective indirect address. If X were equal to
A in this example, the effective indirect address would be $DDC4, and
the contents of $DDC4 and $DDC5 would be used as the effective
address of the data.

$00 / / $FF \

INDEXED, INDIRECT ALLOWS ONE OF SEVERAL


INDIRECT ADDRESSES TO BE CHOSEN USING
THE X INDEX REGISTER

Figure 5.9

In certain types of control processing, this is a quite useful address mode.


X will contain an even number; since each indirect address is two bytes
long, we will need to skip from one to the other, two bytes at a time.

Let's take a hypothetical communications system that is connected to four


telecommunications lines and see how indexed, indirect addressing might
be used. Characters are being received from the four lines almost simul
taneously. As each character arrives, it must be put away into a memory
buffer belonging to that particular line; in that way, traffic received from
the various sources won't get mixed together. Zero-page will contain four
indirect addresses, one for each line; each indirect address points at an
input area for one line. Suppose a character is received into the A register
from one of the lines; the line number (times two) is in the X register. We
84 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

The above example is a rather specialized use of the indexed, indirect


address mode. You may never need to use this mode. Indeed, most
programmers lead full, rich lives without ever writing code that uses in
dexed, indirect addressing.

The Great Zero-Page Hunt


Indirect, indexed addresses are very important. They are your gateway to
reaching any part of memory from a single instruction. But you must have
two bytes available in zero-page for each indirect address you want to
use.

The Commodore ROM system helps itself to liberal amounts of zero-page


memory. You don't have much empty space left over. How can you find
space for these indirect pointers?

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.

Project: Screen Manipulation


This project is intended to show how indirect, indexed addressing can be
used effectively. We'll change something on the screen—enough so that
we reach more than 55b addresses. Ordinary indexing, therefore, won't
do.
ADDRESS MODES 85

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.

Since there's a variety of Commodore machines, we have some problems


to resolve. Except for the C128 in 80-column screen mode, all Commodore
screens are "memory mapped," that is, the information appearing on the
screen is copied directly from some part of memory. We may change the
screen by changing the appropriate memory. But different machines use
different memory addresses; and in VIC and Commodore 64, the screen
may be moved around. Another thing to consider is that the length of line
varies between different machines—it might be E E or A D or fl D columns.

C128 note: Remember to check Appendix E, Exercises for the Commo


dore 128, for the appropriate coding.

No problem. If you have a 40-column machine, AQ equals $Efl; code

fl D33C LDfl #$Efl

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:

PET/CBM: fl D33E LDX #$flD


fl D34D NOP
VIC/Commodore 64: fl D33E LDX $DEflfl

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

A D341 STA $03AD


A D344 STX $BC

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:

A D34b LDA #$DD


A D34Q STA $BB

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

A D34A LDX #$DD

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:

A D34C LDY #$D<

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:

A Q34E LDA ($BB) ,Y

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:

A 0350 CMP #$20


A 0352 BEQ $035b

We have to guess at the address to which we will skip ahead, since we


haven't gotten there yet. Make a note: "This address may need correction."

A 0354 EOR #$flD

This is where we manipulate the character. The EOR is a "flip-over"


command; we're flipping the high bit of the screen value. You may look
up screen codes to see what this does, or you may wait and see what
happens. At this point, our code from $0352 joins up. As it happens, we
were lucky again: the address is exactly right to rejoin at $035k. But if
it were not, you know how to fix it, don't you? Exit the assembler, then go
back and type over.

Now we put the modified character back to the screen:

A 035b STA ($BB),Y

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

a D3t3 ST& $BB


A D3&>5 LDA $BC
A D3b7 ADC #$DD
STA $BC

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

If we've done the required number of lines, we have nothing more to do


other than return to BASIC:

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.

Project for enthusiasts: Can you change the program to do a different


set of columns? Could you change it so that it affected only the letterfl S "
wherever it appeared on the screen?

Comment for VIC-20 and


Commodore 64
This exercise will work as intended. Other types of screen work might call
for you to set the color nybble memory values before you can successfully
work directly with screen memory. The rules for machine language are no
different from those for BASIC: if you wish to POKE to the screen, you
may need to take the color nybble area into account.
ADDRESS MODES 89

Things You Have Learned


—Three address modes are not addresses at all. Implied addressing means
no address at all; accumulator addressing uses the A register and means
the same thing; and immediate addressing uses a value, not an address.
—Absolute addresses reference one location only, somewhere in memory.
Zero-page addresses reference a single address in the range $DDDD to
$DOFF—the high byte of the address (DD) is the memory page. These
address modes are used for fixed locations containing work values or system
interfaces.
—Absolute, indexed and zero-page, indexed allows the named address to be
adjusted by the contents of an index register—X or Y. These instructions can
reach a range of up to 25fc addresses. They are commonly used for tables
of data or temporary storage areas.
—Relative addresses are used exclusively with branch instructions. They have
a limited "reach" of about 127 locations forward or backward. It takes a little
arithmetic to calculate the proper values, but the computer usually works this
out for us.
—Indirect addressing is used only for jumps, most often to allow a fixed ROM
program to take a variable jump. The average machine language programmer
will seldom need these, but the principle of indirect addressing is worth learning.
—Indirect, indexed addressing is the most important way to deal with data
anywhere in memory. We may reach anywhere by setting the indirect address,
then we may "fine adjust" that address by indexing it with the contents of Y.
—Indirect, indexed addressing requires the indirect address to be in zero-page.
We need to conserve zero-page locations for this use.
—An addressing mode called indexed, indirect is rarely used when program
ming Commodore computers, but it's there if you want it.

Questions and Projects


Write a program to clear the screen of your computer—check Appendix
C for the location of screen memory if you've forgotten. Don't just print
the clear screen character ($R3); do it another way. Can you write the
entire program without using indirect, indexed addressing?
Write the program again using indirect, indexed addressing. The program
may be a little shorter. Can you think of any other advantages of writing
this way?

A user wishes to type in a line of text on the keyboard, ending with a RE T U R N.


He then wants to have the program repeat the line ten times on the screen.
What addressing mode or modes would you use to handle the user's text?
Why? You may try your hand at writing the program if you wish.
Take one of the previous exercises and try to write it again without using
immediate addressing. Is it hard to do? Can you see any reason to want
to code without using immediate addressing at all?
■PV
6
Linking
BASIC and
Machine
Language
This chapter discusses:

• Where to put a machine language program


• BASIC memory layout
• Loading and the SOV pointer
• BASIC variables: fixed, floating and string
• Exchanging data with BASIC
92 MACHINE LANGUAGE FOR COMMODORE MACHINES

Siting the Program


Up to this point, we have been placing all programs in the cassette buffer.
This is a good place for short test programs, but we need to examine
alternatives that are often more attractive.

BASIC Memory Layout


C128 and B-128 note: These two machines keep variables in a separate
memory bank from that in which the BASIC program is held. Some of the
considerations described below—especially regarding dangers with the
Start-of-Variables pointer—don't apply. For C128 details, check Appendix
E, under Exercises for the Commodore 128.

BASIC RAM is organized according to the diagram below. The following


locations are of particular interest:

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

CASSETTE BASIC BASIC BASIC DYNAMIC


0 FREE
BUFFER PROGRAM VARIABLES ARRAYS STRINGS

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.

4. Start-of-variables (SO V) is often positioned directly behind the end-of-BASIC.


When the BASIC program runs, the variables will be written into memory
starting at this point; each variable is exactly seven bytes long. A pointer
marks this location. The pointer is located at $EA/$EB (decimal 42 and
43) in PET/CBM, and at $ED/$EE (decimal AS and 4b) in VIC-20, Com
modore 64, and PLUS/4.

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.

If the BASIC program is changed, the SO V may move up or down as needed.


5. Start-of-arrays (SO A) also represents one location beyond the end-of-BASIC
variables, and thus could be named end-of-variables. Arrays created by the
BASIC program, either by use of a DIM statement or by default dimensioning,
will occupy memory starting at this point. A pointer marks this location. The
pointer is located at $EC/$ED (decimal 44 and 4S) in PET/CBM, and at
$EF/$3D (decimal 47 and A&) in VIC-20, Commodore 64, and PLUS/4.
If the BASIC program is changed, the SO A pointer is set to match the SOV.
Thus, all BASIC variables are wiped out the moment a change is made to
the program.

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.

Free Memory: The Dangerous Place


It seems to beginners that there is a great deal of free memory available
above the end-of-arrays and below the bottom-of-strings, and that this
would be an ideal place to put a machine language program. This is a
pitfall: it usually won't work.

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

garbage collection: except on BASIC 4.0 and Commodore PLUS/4 sys


tems, it can be a cause of serious program slowdown.

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.

Where to Put Your ML Program


First, you may put your program in the cassette buffer. Providing you are
not performing input/output activity, your program will be safe. Your space
here is limited to 1RD characters or so.

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.

SOB SOV SOA EOA BOS NEW OLD


I III TOMTOM

C.B.

Figure 6.3

SOB OLD NEW SOA

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.

Extras for VIC and Commodore 64


The Commodore 64 has a free block of RAM at locations $CDDD to
$CFFF (decimal 4^152 to 53E47). That's 4K of RAM not being used;
you may write your programs there. Before you do so, check to make sure
that the memory is not being used by any other programs. It's a popular
place in the Commodore 64, and many utilities and commercial programs
zero in on this available memory.

If you intend to write programs entirely in machine language, with no BASIC


content at all, you may completely remove BASIC from the Commodore
64 system and claim the space as available RAM. This gives you the
whole block from $DflDl up to $CFFF for programs and data—a whop
ping 50K—and even more could be liberated if necessary. BASIC may
be made to disappear from the Commodore 64 with the equivalent of
POKE 1,54 (LDA #$3b, STA $D1). It may be reinstated with the
equivalent of POKE 1, 55 (LDA #$37, STA$D1). Be very careful.
With BASIC gone, the computer doesn't even know how to say READY.

On all Commodore machines it's possible to move up the start-of-BASIC


pointer and use the space freed below. To do so, it's essential to store a
value of zero into the location immediately before the new start-of-BASIC,
and to align all other pointers, usually by going to BASIC and commanding
NEW.

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 VIC-20 can also get information from $flOOOto$cIFFF,but there's


no RAM there; we can't manipulate this memory area.

If we want to perform special visual effects on the VIC-20, we must ma


nipulate data in the area $DDDDto$lFFF. Let's look at what is available.
$0000 to $03FF is used by the "system;" other than the cassette
buffer, we must leave it alone. $0400 to $0FFF contains no memory
unless a 3K RAM expansion is added. $1000to$lDFF contains the
BASIC program, and $1EOO to $1FFF is screen memory. Details may
vary, but the answer always comes out the same: there's no space to do
our video effects.

A popular VIC-20 solution, especially where 8K or more of RAM expansion


has been added, is to increase the start-of-BASIC pointer, thus liberating
space in low memory. This may now be used for visual effects and for
machine language programming, too, if any space is left over. In the VIC-
20, this approach is necessary, but it's still a bit clumsy.

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.

4. Changes to BASIC programs cause memory to be moved—up or down—


starting from the point where the change is made and stopping at the SOV.
The SOV will then be moved the appropriate distance up or down.

These seem to be innocent rules. Rule 1 defines the purpose of the SO V.


Rule 2 shows how the SOV controls the SAVE command so that the
entire BASIC program is saved, but not the variables. Rule 3 arranges
that short programs will have a large amount of variable space available;
long ones will have less. Rule 4 ensures that a BASIC change makes
extra room in memory or reclaims memory space.

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 have a program in the cassette buffer, and a BASIC program that


goes with it. We enter or load the BASIC program (the SOV is all right so
far), and then we LOAD the machine language program; the SOV ends
up disastrously somewhere in the cassette buffer area.

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 more trouble. Alternatively, the programmer decides to save his


BASIC program and commands SAVE. BASIC starts to save memory
beginning at start-of-BASIC... and keeps saving, and saving, and saving.
It won't stop until it reaches the SOV, but that's below where we started.
We won't get there until the address "wraps around" and comes back up
through zero. The poor programmer—if he or she waits long enough—
discovers that the tiny five-line BASIC program has been saved as over
25 D blocks on disk, or fifteen minutes worth of tape. And the saved
program is useless.

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

shake your self-confidence. Many programmers have given up on machine


language because of a bad experience with SOV .

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.

Our eager student—with a perfect machine language program and a per


fect BASIC program—now decides to say RUN. The BASIC program runs
for a while, and then grinds to a halt, usually with a crazy screen or reporting
an error in a nonexistent line. We know what's happened, of course: the
variables have started to write over the BASIC program. But our unfor
tunate student doesn't know that. The command LIST is entered, and
out comes nonsense.

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.

The Machine Language Monitor SAVE


Now that we're becoming aware of the SO V pitfall, we're ready to discuss
how to save a program in machine language. You probably understand
why I've been delaying this command until this time. The MLM save com
mand typically goes

S "PROGRAM",Dl,D33C,D3bl

This would be the tape format. The command is S and is followed by


the program name. The device is tape, so we type Dl—be sure to give
two digits. Next comes the beginning address (in the example $D33C)
700 MACHINE LANGUAGE FOR COMMODORE MACHINES

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:

S "D:PR0GRAM", Dfi, D33C, D3bl

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.

The L command is of limited value. A program user often cannot be


expected to load up a machine language monitor and use it to go through
a L load sequence. The program should take care of things for the user.

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.

Programmed LOAD has been carefully designed to perform a function


called "chaining." That's a BASIC technique, and not within the scope of
this book. Chaining, however, has two important characteristics:

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.

If we understand the problem that item 2 creates, we can easily fix it by


using item 1. Here's an example to illustrate the problem: we have a
program on disk written for the cassette buffer called fl ML ", and we want
to have a BASIC program bring it in. We could code as a first line: 1DD
LOAD " ML M / fl—but we'd have a problem. First, the program would load
LINKING BASIC AND MACHINE LANGUAGE 101

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:

1DD IF A = l GOTO 13D


11DA=l
150 LOAD »ML»,fl,l
13D . . . continues

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.

Caution: we are discussing the programmed LOAD command only in the


context of loading machine language modules. If you want to have a
program load in another BASIC program (chaining or loading) the above
rules still apply but may need to be used differently.

Other SOV Blunders


We have discussed the horrible results of loading a machine language
program into the cassette buffer (using a direct command) after BASIC
has been loaded. By now, we should have learned to avoid making this
mistake. What about programs stored in other areas, such as high memory
or after BASIC?

Suppose we want to place a program into high memory, either by moving


the top-of-memory pointer down to make room, or by using the spare RAM
at $C000 to $CFFF of the Commodore 64. We also have a BASIC
program to load. Will loading in the wrong order harm SOV?

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.

Obviously, we can't leave SOV in the upper stratosphere. We must load


the high memory first, and then the BASIC program. The second load will
straighten out the SO V pointer. If you try this, you'll find that it is necessary
to fix up the top-of-memory pointer and command NEW between the two
702 MACHINE LANGUAGE FOR COMMODORE MACHINES

loads; you cannot even give the next LOAD command if you're apparently
totally out of memory.

Review: Fixing Pointers


If in doubt, examine the pointers by displaying them with a M command.
For VIC/64/PLUS/4, the command would be M DDEB DD3A; with
PET/CBM, use M DDES DD37; in either case, be sure that the start-
of-variables pointer is set to a "sound" value.

As always, you can change an incorrect memory value—in this case, an


incorrect vector—by moving the cursor back, typing over the values to be
changed, and pressing RETURN.

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.

There's one small problem in this arrangement. Once we have married


the BASIC and machine language programs, we must not change the
BASIC program. If we added to or subtracted from this program, the
machine language program would move up or down—the relocation of
memory goes right up to SOV. The program might not be able to work in
the new place, and, of course, our SYS commands would be wrong.

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

EACH VARIABLE IS EXACTLY 7 BYTES LONG.


VARIABLES APPEAR IN THE ORDER IN
WHICH THEY ARE USED.
Figure 6.5

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.

HIGH BIT SET FOR INTEGER VARIABLES AND FUNCTIONS

HIGH BIT SET FOR INTEGER AND STRING VARIABLES

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.

Let's try an example. Type NEW, followed by A = 5 = : B% = 5. This cre


ates two different variables: A and B%. Now go to the machine language
monitor. The variables should be near the start-of-BASIC, but if you wish
you can find their exact address by examining the SOV pointer ($2A/
$EB on PET/CBM, or $2D/$EE on VIC, Commodore 64 or PLUS/4). On
the Commodore 64, we might find that the variables start at $DflD3; to
display both of them, we type M DflD3 DfllD. We see the floating
point variable, A:

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.

Behind this variable, we see the integer variable, B:

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.

Which is easier for machine language to interface with? Obviously, the


integer variable. It's often quite suitable for the program work at hand:
counting characters, setting pointers, and similar tasks.

Exchanging Data: BASIC and Machine


Language
If BASIC and machine language wish to pass data back and forth, there
are several approaches. Perhaps the simplest is to have BASIC POKE
the values into a given location, and machine language load them as
needed; in the opposite direction, machine language will store the values
and BASIC will PEEK them.

Another method is more sophisticated. BASIC variables are stored in


LINKING BASIC AND MACHINE LANGUAGE 705

memory: why can't a machine language program go after the variables


exactly where they lie and extract their value or change them? It sounds
like a good idea.

By now, we know how to ask machine language to search for a specific


BASIC variable. Given the name, we can get the address of the first
variable from the SOV pointer and store it as an indirect address. Using
indirect, indexed addressing and stepping the Y register from D to 1 we
can see if the name matches. If not, we add seven to the indirect address
to take us to the next variable. If it does match, our indirect address is set
up at the start of the variable; we can set Y to 5, 3 , 4 , 5, and t and
extract the whole value. If the variable is type integer, we need only extract
the first two bytes (Y = 5 and 3). If the variable is not in the variable table,
we'll step our indirect address until it matches the start-of-arrays; at that
point, we know that we have missed the variable.

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.

C128 note: Remember to check Appendix E, under Exercises for the


Commodore 128, for the appropriate coding. There's a lot here on how
to dig out information from another memory bank.

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:

A DflflD LDY #$D2


A DflflE LDA ($2D), Y
A DflflD STA $D33C
A Dflfl? STA $Q33E
A DflfiA LDY #$D3
A DflflC LDA ($SD), Y
A DflflE STA $D33D
A DflRl STA $D33F

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.

a Dflq< asl $D33D


a Dfiq7 ROL $D33C
a Dflqa asL $D33D
a DfiSD ROL $D33C

We have multiplied the contents of $D33D/$D33C by two, and then we


have multiplied it by two again. These locations now contain the original
value times four. Note that we aSL the low byte and then ROL the high
LINKING BASIC AND MACHINE LANGUAGE 107

byte. Perhaps we should be checking for overflow; but let's trust the num
ber to be within range for now.

Since we have the original number times four in $ D 3 3 D/$ D 3 3 C, we can


add it to the original number in $ D 3 3 F/$ D 3 3E to get the original number
times five:

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

Now locations $D33C/$D33D contain the original number times five. If


we double the number one last time, we'll have the value times ten:

A DflB3 ASL $D33D


a QflBb ROL $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.

We must perform one more task before wrapping up the program. We


must change the start-of-variables pointer to a location above the machine
language program. That would be $DflCfl, and so we display the SOV
pointer with M DDED DDEE and change the pointer to

:DDED Cfl Dfl

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

Things You Have Learned


—Small machine language programs can be conveniently written and checked
out in the cassette buffer. We have been doing this during the exercises. This
area is not satisfactory for large programs, or programs we want to save on
tape.
—Programs can take up semi-permanent residence near the top-of-BASIC
memory; the top-of-memory pointer needs to be moved down to protect it.
These programs often need a separate "setup" to place them.
—Programs can be placed behind the end-of-BASIC, which is marked by three
consecutive zero bytes in memory. The start-of-variables pointer must be
increased so that variables don't write over the program. Care must be taken
not to change the BASIC program after this is done.
—The VIC-20 frequently has the start-of-BASIC moved up to make room for
video information in lower memory. As long as we're moving this pointer, we
might move it a little further and make room for some machine code.
—The Commodore 64 has an unused block of RAM at addresses $CDDD to
$CFFF; check to see that no other programs are using this area.
—The start-of-variables pointer is intimately tied in with BASIC'S SAVE and
LOAD commands. It is extremely important to ensure that any LOAD se
quence leaves this pointer in a safe place, so that variables cannot write over
program code and thus cause program destruction.
—Machine language monitor S (save) and L (load) commands can be used
for staging programs in various parts of memory. Again, great care should
be taken to ensure that the pointers are sound after the use of such instruc
tions.
—A BASIC program may contain LOAD commands that will bring in any of the
following: a different BASIC program, a machine language program, or data.
Again, careful handling is needed.
LINKING BASIC AND MACHINE LANGUAGE 709

—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.

Questions and Projects


Write a simple BASIC and machine language program set that allows
BASIC to input a number less than 5Sb; POKE it somewhere in memory;
call machine language that will divide the number by two; PEEK it back
and print it.

A program that brings in other programs is called a "boot," or, more


accurately, a bootstrap program. Write a simple BASIC boot program to
bring in a previous program exercise that was located in a cassette buffer
(say, the program from Chapter 2 that printed HELLO), and then call it
with a SYS.

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.

Try your hand at this. I have a BASIC program that reads


1DDX = 5
11D SYS . . .
1ED PRINT A

Write the machine language to be called by the SYS so that it changes


the name of the variable X to A. Caution: this may be fun, but it's dangerous
in real programs since you may end up with two variables that have the
same name.
7
Stack, USR,
Interrupt,
and Wedge
This chapter discusses:

• The stack for temporary storage


• USR: an alternative to SYS
• Interrupts: IRQ, NMI, and BRK
• The IA chips: PIA and VIA
• Infiltrating BASIC: the wedge

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.

Temporary Storage: The Stack


The stack is a convenient place to put temporary information. It works like
a stack of documents: you may drop (or "push") an item onto the stack;
when you take an item back again (or "pull"), you'll get the last one that
you put there. Formally, it's called a last-in, first-out (LIFO) discipline; it's
natural and easy to understand.

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

stack—whether or not they had been put there before—the processor


would happily move the stack pointer round and round page 1, delivering
bytes. There would only be 551 different values delivered, of course, but
the processor doesn't care.

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

PHA (push A) and PLA (pull A)


How may we use the stack? Suppose we have a value in the A register
andin a moment we will want to use it. First we need to print something,
andthe character to be printed must be loaded into the A register. How
can we put away the value in A and bring it back later? We could slip it
into another register with a transfer instruction (TAX or TAY) and bring
it back from there; or, we could store it into memory and load it back.
Alternatively, we could PUSH the A register (PHA) to the stack and PULL
(PLA) the value back later.

Again, let's do an example. Suppose the A register contains 5, and the


114 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

It's a handy way to put away a value in A for a moment.

PHP (push processor status) and PLP


Sometimes when we are writing a program, we want to test for a condition
now but act on the result of that test later. We can arrange to do this by
putting the flags away for the time being, and then bringing them back
when we want to test the flags. We use the instruction PHP (push the
processor status word) to place all the flags on the stack, and PLP (pull
the processor status word) to restore the flags to the status register (SR).

Why would we need to do this? Perhaps an example will illustrate. Suppose


we are reading a file of customer purchases, and as we input a data item,
we discover that this is the last one—it's the end of the file. That means
that we want to close the file and summarize the customer's activity—
though not just yet. First, we must handle the item of information that we
have input. So we can "stack" our end-of-file information, handle the last
record in the same way as previous records, then bring back the status
to see whether it's time to close the file and print the totals. We'll be using
PHP and PLP for exactly this kind of task in the next chapter.

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.

JSR and RTS


We know these commands. What are they doing here?

When a JSR command is executed, the return address is placed onto


the stack. When an RTS command is executed, the return address is
picked from the stack, and that's where the program returns to.

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.

Example: If address $D355 contains the command JSR $D33C, the


STACK, USR, INTERRUPT, AND WEDGE 115

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.

Interrupts and RTI


There are three types of interrupt: IRQ, NMI, and the BRK instruction.
IRQ (interrupt request) and NMI (non-maskable interrupt) are pins on
the 650x. A suitable signal applied to the appropriate pin will cause the
processor to stop what it's doing and run an interrupt routine. The BRK
instruction might be thought of as a fake interrupt—it behaves in a similar
manner to IRQ.

When an interrupt signal occurs, the processor completes the instruction


it is currently working on. Then it takes the PC (the program counter, which
contains the address of the next instruction) and pushes it onto the stack,
high byte first. Finally, it pushes the status register to the stack. That's a
total of three bytes that go to the stack.

The processor then takes its execution address from one of the following
locations:

IRQ or BRK—from $FFFE and $FFFF

NMI —from $FFFA and $FFFB

Whatever value is found in these pointers becomes the interrupt execution


address: the processor starts to run at this address. Eventually, the pro
cessor encounters an RTI instruction. The status register and the PC
address are taken from the stack, and the interrupted program resumes
where it left off.

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

Plus-4 series; and on VIC-20, Commodore 64, and Commodore 128, it is


used for the RESTORE key and for RS-232 communications.

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.

Mixing and Matching


The processor uses the stack mechanically. If we know how to manipulate
the stack, we can use it for surprising things. For example, an RTS can
be given even though there was no subroutine call; all we have to do is
prepare the stack with the proper address. Try to figure out what the
following code will do:

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.

U SR: A Brother to SYS


We have used SYS a number of times. It means, "Go to the address
supplied and execute machine code there as a subroutine." U S R is similar
STACK, USR, INTERRUPT, AND WEDGE 777

in many respects: it means, "Go to a fixed address and execute machine


code there as a subroutine." The fixed address may be POKEd into the
USE vector. On most Commodore machines this is at addresses 1 and
2; on the Commodore 64, it's at addresses 7fl5 and 7flb (hex D311
and D31E).

There's another difference that seems important at first. SYS is a com


mand; USE is a function. You cannot type the command USR ( D )—all
you'll get is SYNTAX ERROR. You must say something like PRINT
USR ( D ) or X = OSR ( D ), where USR is used as a function. It seems
as if SYS was meant to connect to action programs, and USR was meant
to link to evaluation programs. In reality, the difference in usage is not that
great.

Whatever value is within the parentheses—the argument of the US R func


tion—is computed and placed into the floating accumulator before the
USR function is called. The floating accumulator is located at $5E to $b3
in most PET/CBM computers, and at $fcl to $tfc in VIC-20, Commodore
64, and PLUS/4. Floating-point representation is complex, as we have
hinted in Chapter 6. Most beginning programmers prefer to leave this area
alone and pass values through memory POKEs or integer variables.

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.

Interrupts: NMI, IRQ, and BRK


We have mentioned the mechanical aspects of interrupt. Now let's look
at how to use the interrupt for simple jobs.

The IRQ connects through a vector in RAM; if we change the address


within the vector, we will change the address to which the interrupt goes.
The interrupt vector is located as follows:

Most PET/CBM: DDRO-DDRl (decimal

VIC/Commodore 64: D314-D315 (decimal 7flfl-7flcl)

Before we change this vector, we should realize something quite important:


the interrupt does a lot of work sixty times a second. It updates the clock,
checks the RUN/STOP key, gives service to the cassette motors, flashes
the cursor, and handles keyboard input. If you thoughtlessly change the
IRQ vector, it will stop doing these things; and it's hard to handle a
118 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

Care must be taken in changing an interrupt vector. Suppose we are


beginning to change the two-byte address; we have changed the first byte,
and suddenly, the interrupt strikes. It will use an address that's neither
fish nor fowl: half is the old address, and half is the new. In such a case,
it's likely that the interrupt will become confused; and if the interrupt is
confused, the whole computer is in trouble. We must find a way to prevent
interrupt from striking when we change the vector.

We could do this in machine language: before a routine to change the


IRQ vector, we could give the instruction SEI (set interrupt disable). After
this instruction is given, the IRQ cannot interrupt us. We may set the
vector and then re-enable the interrupt with the instruction CLI (clear
interrupt disable). Be sure that you do this, since the interrupt routine
performs many vital functions. We may say that we have masked off the
interrupt in the time period between execution of SEI and CLI. The NMI
interrupt, however, is non-maskable, and SEI will have no effect on it.

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.

Almost all interrupt signals are delivered through an IR (interface adaptor)


chip; and these chips invariably allow the path of the interrupt signal to
be blocked temporarily. We'll discuss the I a chips later; for the moment,
the normal interrupt signals can be blocked with the following actions:

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

VIC-20: Store $7F into address SRIEE (POKE 371bb,157) to dis


able; store $CD into the same address (POKE 371kb, 1R2) to re-
enable.

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.

A warning about interrupt programs: changing the IRQ vector is likely to


make it difficult to load and save programs. You may need to put the vector
back to its original state before you attempt any of these activities.

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:

A D33C LDA $R1 .


A D33E STfi $04DD
A D341 JMP ($D3aD)

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:

A 0344 LDA $D314


A 0347 STA $03AD
A 034A LDA $D315
A 034D STA $O3A1

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 0351 LDR #$3C


A 0353 STft $0314
ft 035b LDR #$03
ft 035fl STft $D315
ft 035B CLI
ft 035C RTS

We will enable the new interrupt procedure byaSYSto$0344, above


(SYS fl 3 b). Before we give that command, let's write the coding to put
everything back:

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.

The IA Chips: PI A, VIA, and CIA


The interface adaptor (I a) chips are richly detailed. To understand them
fully, you'll need to read the specifications in some detail. Here, we'll give
their main functions.

PI a stands for peripheral interface adaptor, Via for versatile interface


adaptor, and Cia for complex interface adaptor. There is speculation
among Commodore owners that the next interface chip will be called "FBI."

The functions performed by an interface adaptor are:


STACK, USR, INTERRUPT, AND WEDGE 121

. 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.

Latching is important. A triggering event may be brief; so short, in fact, that


the original signal causing interrupt might go away before the processor can
look at it. An IA event flag locks in the signal and holds it until the program
turns it off.

ON. .OFF
INTERRUPTING H LATCH 1
EVENT '
| COMPUTER
EVENT ACKNOWLEDGEMENT
FLAG
Figure 7.2

If an event has time importance—that is, if the event's timing must be ac


curately measured, or if the event flag must be cleared quickly so as to allow
for the detection of a new event—we may link the event flag to the interrupt
line. If we do so, the occurrence of the event will cause the processor to be
interrupted. We must write coding linked to the interrupt routines to detect
this event, clear the flag, and do whatever processing is needed. We set up
this link to the interrupt line by means of a register usually called the interrupt
enable register.

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 IER (interrupt enable register) is often a source of problems. In many


cases, the high bit of a value we are storing has a special meaning: if it's
set, the other bits will cause the appropriate interrupt connections to turn
on; if it's clear, the other bits will cause the appropriate interrupt connec
tions to be turned off. You may recall that we shut off the Commodore 64
interrupt by storing $7F into address $DCDD. This may seem odd: we're
storing a binary value of$Dlllllll, which might seem to be turning
bits on. In fact, the high bit of zero signals that all the remaining bits
are"turn off" signals; so the value causes all interrupts to be blocked.

Infiltrating BASIC: The Wedge


In zero-page, there's a subroutine that the BASIC interpreter uses fre
quently to obtain information from your BASIC program. It's used to get
a character from your BASIC program, and to check it for type (numeric,
end-of-command, or other).

The routine is normally entered at either of two points: CHRGET, to get


STACK, USR, INTERRUPT, AND WEDGE 123

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.

Since CHRGET is in different locations, depending on the machine, the


following coding is shown with symbolic addresses. That is, instead of
showing the hex address value, the address is given a name, or symbol.
Thus, CHRGOT might represent address SDD?^, CHRGOT+ 1 would
represent address $DD7 A, and so on.

CHRGET INC CHRGOT+1


BNE CHRGOT
INC CHRGOT+E
CHRGOT LDA xxxx

This subroutine is self-modifying, that is, it changes part of itself as it runs.


That's not always a good programming technique, but it works well here.
The first part of the subroutine adds one to the address used by instruction
CHRGOT. This is a standard way of coding an address increment: add
one to the low byte of the address; if that makes it zero, the low byte must
have gone from $FF to $DD, in which case, add one to the high byte.
The address loaded by CHRGOT is within your BASIC program, or within
the input buffer if you have just typed a direct command. Before we follow
the next piece of code, let's look at our objectives:

1. If we find a space, go back and get the next character.


2. If we find a zero (BASIC end of line) or a colon (hex $3 A, BASIC end-of-
statement), we wish to set the Z flag and exit.

3. If we find a numeric, we wish the C flag to be clear; if we do not find a


numeric, we wish the C flag to be set.

CHRGOT LDA xxxx


CMP #$3A
BCS EXIT

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

If the character is a space, we go back and get another character.


The following coding looks rather strange, but it's correct. After the two
subtractions, the R register will be back where it started:

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

Breaking Into BASIC


Since BASIC comes to this subroutine often, we can infiltrate BASIC by
changing this subroutine. Extra coding in this area is often called a "wedge"
program. We must be very careful:

• We must leave A, X, and Y unchanged; either we must not use them or we


must save them away and bring them back.
• We must not interfere with the flags.
• We must be careful not to slow BASIC down too much.

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.

In PET/CBM, we may choose to modify this subroutine in either of two


places: near the beginning, in CHRGET; or after the LDR, in CHRGOT.
Each location has its advantages. In the CHRGET area, we don't need
to preserve the R register or status flags, since CHRGOT will fix them up
for us. In the area following CHRGOT, we have the character we wish to
examine in the fi register.

But in either case, it's an exacting job.

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

The address of the instruction at C H R G0 T is often referred to as T X T P T R,


the text pointer. This address always points to the BASIC command being
executed at the moment. If we want to participate in reading BASIC, we
must learn to use TXTPTR to get the information—usually by means of
indirect, indexed addressing—and to leave this address pointing at a suit
able place when we return control back to the normal BASIC handling
programs.

Project: Adding a Command


Let's add a simple command to the VIC and Commodore 64 by using the
$D3Dfl vector. The ampersand (&) character isn't used in most BASIC
programs, so we'll make it mean this: whenever you see the code "&»,
print ten asterisk (*) characters to the computer screen, followed by a
carriage return.

C128 note: Remember to check Appendix E, under Exercises for the


Commodore 128, for the appropriate coding.
As with our interrupt program, we'll copy the old address from $D3Dfl/
D3DC1 into an indirect address location, so that we can link up with the
normal computer routines as necessary.

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.

Here's our instruction "intercept":

ft D33C LDY #$D1

We're going to use indirect, indexed addressing to "look ahead" at the


instruction. Let's look, using TXTPTR as an indirect address:

A D33E LDft ($7ft) ,Y

Since Y equals one, we'll look just beyond the address to which TXTPTR
is pointing:

ft D34D CMP #$Et


ft D345 BEQ $D34?
A D3AA JMP ($D3ftD)

If the character is an ampersand, we'll branch ahead to $D347. If not,


we'll connect through the indirect vector to the regular BASIC interpreter
code:
726 MACHINE LANGUAGE FOR COMMODORE MACHINES

A 0347 JSR $0073

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:

A 035E LDA $030fl


A 03bl STA $03A0
A 03fc4 LDA $D30R
A Q3b7 STA $O3A1
A 03tA LDA #$3C
A 03fcC STA $030fl
A 03bF LDA #$03
A 0371 STA $D30R
A 0374 RTS

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:

1DD PRINT 3 4:&:PRINT 5 + b


11D &
1E0 PRINT "THAT'S ALL"

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.

Infiltrating BASIC isn't an easy job. But it can be done.


STACK, USR, INTERRUPT, AND WEDGE 127

Things You Have Learned


—The stack is located in page 1, from $D1FF moving down to $D1DD. It is
used for holding temporary information. A program may push information to
the stack, and then pull it back later. The last item that has been pushed onto
the stack will be the first item to be pulled back off.
—Great care must be taken to ensure that your program pulls exactly the same
number of items back from the stack as it pushed. In particular, be sure that
a branch or jump does not inadvertently omit a needed stack activity. A badly
handled stack is often fatal to the program run.
—PHA pushes the contents of A to the stack; PL A pulls from the stack into
the A register. These two commands are often used to temporarily save A.
PHP pushes the status register (S R); PL A pulls it back. These two commands
are often used for "deferred decisions."
—JSR pushes a return address (minus 1) to the stack; RTS recalls this ad
dress. We may use JSR and RTS without needing to know the role the stack
plays, since the two commands take care of the details for us.
—Interrupts, including the BRK instruction, push three items to the stack; RTI
brings them back so that the interrupted program may resume.
—USR is a function, as opposed to SYS, which is a command. USR goes to
a preset address, takes a numeric argument, and can return a value. In
practice, USR and SYS are used in quite similar ways.
—Commodore ROM systems contain coding for the interrupt sequences that
cause the data registers—A, X, and Y—to be pushed to the stack, and a
branch to be taken through an indirect address that the user can modify.
Since interrupt is active virtually all the time, it may be used to create activities
that are active even when no BASIC program is running.
—The various IA chips—PI A, VIA, and CIA—perform many different func
tions, including: recording events in latching flags and controlling interrupts;
timing; and connecting input/output ports. The detailed specification sheets
must be studied for these rather complex details.
—A subroutine called CHRGET is used frequently by the BASIC interpreter
when a BASIC program is running. We may modify or add to this subroutine
in order to add to or modify the BASIC language itself.

Questions and Projects


If you redirect the interrupt vector to your own machine language program,
you can copy all of zero page to the screen. Use indexing; start X at zero;
and walk through the whole of zero page, loading the memory contents
and storing (indexed again, of course) to the screen. Don't forget to connect
up your code to the regular interrupt entry address.
728 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

It's sometimes suggested that a good way to pass information to a sub


routine is to push the information onto the stack and call the subroutine.
The subroutine can pull the information from the stack. What's wrong with
this suggestion?

The above suggestion can be implemented, but it takes a lot of careful


stack work. You might like to work through the logic needed to do this.

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?

A SYS command from BASIC is like a subroutine call; so it must place


an address on the stack to allow RTS to return to BASIC. Take a look at
the stack and see if you can determine what address is used to return to
BASIC on your machine.
■J
8
Timing,
Input/Output,
and
Conclusion
This chapter discusses:

• How to estimate the speed of your program


• Input and output from tape, disk, and printer
• Review of instructions
• Debugging

• 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.

Occasionally, it may be important to get fairly precise timing for a machine


language program. If so, the following rules of thumb may be kept in mind:
—All timing estimates are crude if the interrupt routines are still active. The
effect of interrupt on timing can be crudely estimated by adding 10 percent
to the running time.
—Remember to allow for loops. If an instruction within a loop is repeated ten
times, its timing will need to be counted ten times.
—The "clock speed," or memory cycle speed, of most Commodore machines
is roughly 1 microsecond—one millionth of a second. The precise number
varies from one machine to another, and also varies between North America
and other regions.
—Most instructions run at the fastest imaginable speed. Count the memory
cycles, and that's how fast the instruction will execute. For example,
LDA #$DD will need two memory cycles just to get the instruction—and
that's how fast it runs. LDA$D5DD,X will usually take four memory cycles:
three to get the instruction, and one to fetch the data from page 5. Exceptions:
no instruction runs in less than two cycles; and shift/rotate instructions, INC/
DEC, and JSR/RTS take longer than you might expect by this rule.
—Branches time differently, depending on whether the branch is taken (three
cycles) or not taken (two cycles).
—When a page boundary is crossed, the computer needs an extra cycle to do
the arithmetic. If the program branches from $DFE4to$lDE3, there will
be an extra cycle; if we LDA $E4E7, Y, there will be an extra cycle if Y
contains a value of $151 or greater.

Detailed timing values can be obtained from most tables of instructions.

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 :

D33C LDX #$□□


D33E LDA #$DD
D34D AND $1?ED,X
D343 INX
TIMING, INPUT/OUTPUT, AND CONCLUSION 133

D345 CPX
D3A? BCC $D34D
RTS

We may work out timing as follows:

LDX #$DD—executed once: 2

LDA #$DD—executed once: 2


AND SITED,X: 32 times at 4 cycles: 128
68 times at 5 cycles (page cross): 340
IN X—100 times at 2 cycles: 200

CPX #$b4—100 times at 2 cycles: 200


BCC—99 times at 3 cycles: 297
1 time at 2 cycles (no branch): 2

RTS—6 cycles: 6

Total time: 1171 cycles, or slightly over one thousandth of a second. We


might add 10 percent to allow for the effects of interrupt; and since this is
a subroutine, we could also add the extra six cycles needed to perform
the JSR.

Where timing is critical, the interrupt could be locked out with SEI. Be
careful: it's seldom necessary, and is potentially dangerous.

Input and Output


We know that calling the kernal routine CHR00T at $FFD5 will send an
ASCII character to the screen. We may also redirect output to any logical
file.

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

CHKIN ($FFC6) CHKOUT ($FFC9)


SETS THE SETS THE
INPUT SWITCH OUTPUT SWITCH

CLRCHN ($FFCC)
RESTORES BOTH
SWITCHES TO "NORMAL"

Figure 8.1

CLOSE—we simply connect to the file and disconnect, and we can do


this as many times as we want.

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.

The character subsequently sent by $FFD2 is usually ASCII (or PET


ASCII). When sent to the printer, special characters—text/graphics,
width—will be honored in the usual way. Similarly, disk commands can
be transmitted over secondary address 15 if desired; a logical "com
mand channel" file must be previously opened.

Registers: Registers A and X will be changed during the CHKOUT


call. Be sure to save any sensitive data in these registers before calling
CHKOOT.

Status: Status flags may be changed. In most recent Commodore


machines, the C (carry) flag indicates some type of problem with con
necting to the output channel.

To switch output to logical file 1, we would need to follow these steps:


TIMING, INPUT/OUTPUT, AND CONCLUSION 735

1. Load a value of 1 into X (LDX #$01).


2. JSR to address SFFCR.

Once the output is switched, we may send as many characters as we


wish using subroutine $FFD2. Eventually, we must disconnect from the
logical file and return to our default output, the screen. We do this by
calling subroutine CLRCHN at address $FFCC.

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.

Registers: Registers A and X will be changed during the CLRCHN


call. Be sure to save any sensitive data in these registers.

Status: Status flags may be changed. In most recent Commodore


machines the C (carry) flag indicates some type of problem with output.

The logical file concept is important. I may send to any destination-


cassette, printer, disk, or screen—without knowing which device is in
volved. I send the characters on their way and the operating system sees
that they are delivered wherever they need to go.

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.

C128 note: Remember to check Appendix E, under Exercises for the


Commodore 128, for the appropriate coding.

First, we'll open the printer channel in BASIC. Let's use logical file num
ber 1:

1QD OPEN 1,4


11D SYS flEfl
120 CLOSE 1

If you don't have a printer, you may open the file to cassette (OPEN
136 MACHINE LANGUAGE FOR COMMODORE MACHINES

1,1, E) or to disk (OPEN 1, fl, 3, " D : DEMO, S, H"). The machine


language program won't care: it will send to logical file number 1 no matter
what it is; it might even be the screen (OPEN 1, 3). Let's write the coding:
ft D33C LDX #$D1
ft D33E JSR SFFCR

Now the output is connected to logical file 1. Let's say HI:

a U3AI LDft #$4fl


ft Q3A3 JSR $FFDE
ft D34b LDft
a D34fl JSR $FFDE
r D34B LDft #$DD
A Q34D JSR $FFDE
ft D35D JSR $FFCC
& D353 RTS

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.

The character subsequently obtained by $FFE4 into the A register is usually


ASCII (or PET ASCII). A binary zero received from a file usually represents exactly
that: an input character whose value is CHR$(D); this is different from keyboard
GET where a binary zero means "no key pressed." When accessing a file, ST
(address SHD for VIC and Commodore 64, SHb for most PET/CBM) is used
for its usual functions of signalling end-of-file or error. Similarly, disk status in
formation can be received over secondary address 15 if desired; a logical "com
mand channel" file must be previously opened.
Registers: Registers A and X will be changed during the CHKIN call. Be sure
to save any sensitive data in these registers before calling CHKIN.
Status: Status flags may be changed. In VIC and Commodore 64, the C (carry)
flag indicates some type of problem with connecting to the input channel.
TIMING, INPUT/OUTPUT, AND CONCLUSION 737

To switch input to logical file 1, we would need to follow these steps:


—Load a value of 1 into X (LDX #$D1)

—JSR to address $FFCb.

Once the input is switched, we may obtain as many characters as we wish


using subroutine $FFE4. Eventually, we must disconnect from the logical
file and return to our default input—the keyboard. We do this by calling
subroutine CLRCHN at address $FFCC. This is the same subroutine that
disconnects output from a logical file.

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:

Disk: OPEN 1,£,3,"D:DEMO,S,W"


Cassette: OPEN 1,1/1

C128 note: Remember to check Appendix E, in the section, Exercises for


the Commodore 128, for the appropriate coding.

This may be done with a direct statement. Now let's write a few things to
the file:

PRINT#1, "HELLO THIS IS A TEST"


PRINT#1,"THIS IS THE LAST LINE"
CLOSE 1
If we have typed in the above statements correctly, we should have a
completed sequential file written on cassette or disk. Before writing the
machine language input program, let's examine how we might read the
file back in BASIC:

Disk: 1DD OPEN 1, fl , 3 , "DEMO"


Cassette: 1DD OPEN 1
110 INPUT #1,X$
12D PRINT X$

130 IF ST = D GOTO 110


140 CLOSE 1

We might alternatively have written lines 11D and 1SD as

11D GET #1,X$


15D PRINT X$;

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.

Type NEW and enter the following program:


Disk: 1DD OPEN I, fl / 3 , "DEMO11
Cassette: 1DD OPEN 1
11D SYS flEfl

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 D33C LDX #$D1


R D33E JSR $FFCb
Now the input is connected to logical file 1. Let's get information from it
and put it on the screen:

a D341 JSR $FFE4


a D344 JSR $FFDE
We must check ST as we would in BASIC. ST might be at either of two
addresses, depending on the system:
VIC, Commodore 64: AD347LDA$C1Q
CBM/PET: A Q34? LDA

If ST is zero, there is more to come from the file; we may go back. If ST


is nonzero, there could be an error or we may be at the end of the file. In
either case, we don't want to read more from the file.

a D34R BEQ
a D34B JSR $FFCC
a D34E RTS

Check it and try it. The file is delivered to the screen quickly.

A File Transfer Program


Let's write a program to transfer a sequential file from any common device
to any other. BASIC will sort out which files to handle; once the files are
opened, machine language will take from and deliver to the appropriate
logical devices as desired.

C128 note: Remember to check Appendix E, in the section, Exercises for


the Commodore 128, for the appropriate coding, both BASIC and machine
language.
TIMING, INPUT/OUTPUT, AND CONCLUSION 739

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.

Here comes BASIC:

1DD PRINT "FILE TRANSFER"


11D INPUT "INPUT FROM (DISK/ TAPE)" ; A$
1ED IF LEFT$(A$,1) = "T" THEN OPEN 1:GOTO ibD
13D IF LEFT$(A$,1)<> "D" GOTO 11D
14D INPUT "DISK FILE NAME" ;N$
15D OPEN l,fl,3,N$
ltD INPUT "TO (DISK, TAPE, SCREEN)";B$
17D IF LEFT$(B$,1) = "S" THEN OPEN E,3:G0T0 E4D
iflD IF LEFTS(B$,l) = "D" GOTO E1D
ISD IF LEFT$(B$,1) <> "T" GOTO ifcD
EDD IF LEFT$(A$,1)="T" GOTO ibD
E1D INPUT "OUTPUT FILE NAME";F$
BED IF LEFTS(B$,l) = "D"
THEN OPEN E,fl,4,"D: " + F$ + ",S,W"
E3D IF LEFT$(B$,1) = "T" THEN OPEN E,1,1,F$
BAD SYS xxxx
E5D CLOSE ErCLOSE 1

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

start-of-variables pointer. We go back and change 24DtoSYS25bD;


now we're ready to put in the machine language code:

A DADO LDX #$D1


A DAD2 JSR $FFCb
A DAD5 JSR $FFE4

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:

A DADfl LDX $RD


A DADA PHP

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.

The next thing we want to do is to disconnect the input by calling $FFCC;


but this will destroy the A register. How can we preserve this value? By
transferring to another register, or by pushing A to the stack. Let's do that.
There will now be two things on the stack.

A DADB PHA

We are now free to disconnect from the input channel and connect to the
output. Here we go:

A DADC JSR $FFCC


A DADF LDX #$D5
A DA11 JSR $FFCq
A OMA PLA

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 DAIS JSR $FFDE


A DAlfl JSR $FFCC

Now we may pick up on the condition of ST that we stacked away earlier.


Here come the flags that we stored:

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:

A DA1C BEQ $DADD


ft DftlE RTS

Important: Before running this program, be sure to move the start-of-


variables pointer ($DD5D/$DDZE) so that it points at address $DA1F;
otherwise, the BASIC variables will destroy this program.

Review: The Instruction Set


We started with the load, save and compare for the three data registers:
LDA LDX LDY
STft STX STY
CMP CPX CPY

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:

AND ORA EOR ADC SBC

Arithmetic also includes the shift and rotate instructions, which may be
used on the A register or directly upon memory:

ASL ROL LSR ROR

Memory may also be directly modified by the increment and decrement


instructions, which have related instructions that operate on X and Y:

INC DEC
INX DEX
INY DEY

We may transfer control by means of branch instructions, which are all


conditional:

BEQ BCS BMI BVS


BNE BCC BPL BVC

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:

JSR RTS RTI

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.

SEC SEI SED


CLC CLV CLI CLD

We may transfer information between the ft register and X or Y; and for


checking or setting the stack location, we may move the stack pointer to
X, or X to the stack pointer. The latter is a powerful command, so use it
with care.

TAX TAY TSX


TXA TYA TXS

We may push or pull information from the stack:

PHA PHP
PLA PLP

There's a special test, used mostly for checking IA chips:

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.

To help us write more ambitious programs, we may wish to turn to com


mercially available assembler systems that allow labels or symbolic ad
dresses. If we wish to write code to call a subroutine to input numbers—
we might not have written this subroutine yet—we can code JSR NUMIN.
When we write the subroutine, we'll put the identifying label NUMIN at the
start. As your program is assembled, the proper address of NUMIN is de
termined, and this address will be inserted as needed.

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.

Where To Go From Here


Almost anywhere. Up to this point, we've been building confidence: trying
to give you a feel as to how the pieces work. Now, the real fun—the
creative programming—is up to you.

Users have varying objectives. You may want to do mathematical oper


ations. You may want to interact upon BASIC programs—analyzing,
searching, renumbering. Whatever suits you. Your interest area may be
music, graphics, or animation. Machine language will open the door to all
of these; its amazing speed makes spectacular effects possible. You may
plan to go into hardware and interface new devices to your computer; an
understanding of machine language, and IA chips in particular, will be
useful. The possibilities are endless.
TIMING, INPUT/OUTPUT, AND CONCLUSION 145

Even if you have no immediate plans to write new programs in machine


language, you will have gained an insight into the workings of your ma
chine. Everything that the machine does—BASIC, kernal, everything—is
either hardware or machine language.

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.

What You Have Learned


-Machine language programs can have run times estimated fairly accurately.
In many cases, however, machine language is so fast that detailed speed
calculations are not needed.

-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.

-Be careful not to confuse connecting to a channel with opening a file. In a


typical program, we open a file only once, but we may connect to it and
disconnect from it hundreds of times as we read or write data.

-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.

Questions and Projects


Write a program to read a sequential file and count the number of times
the letter "A" (hex 41) appears in the file. Use a BASIC PEEK to print
the value. You may assume that "A" will not appear more than 255
times.

Rewrite the above to count the number of occurrences of the RETURN


character ($ DD) in a sequential file. Allow for up to 15 5 3 5 appearances.
Can you attach a meaning to this count?

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:

OPEN 15,0,15: PRINT#15, "SD : JUNK". Convert the PRINT#


statement to machine language and write a program to scratch JUNK.
Careful: don't scratch a program that you will need.

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:

On the 6502, the addresses are normal memory.


On the 6510 and 7501, address D is a directional register and address 1 is an
input/output register, used for such things as cassette tape and memory control.
On the 6509, address D is used to switch program execution to a new memory
bank; address 1 is used to switch the memory bank accessed by the two
instructions LDA ( . . ), Y and STA ( . . ) / Y.

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.

Immediate Addressing—In immediate addressing, the operand is con


tained in the second byte of the instruction, with no further memory ad
dressing required.

Absolute Addressing—In absolute addressing, the second byte of the


instruction specifies the eight low order bits of the effective address while
the third byte specifies the eight high order bits. Thus, the absolute ad
dressing mode allows access to the entire 64K bytes of addressable mem
ory.

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.

Indexed Zero Page Addressing—{X, Y indexing)—This form of address


ing is used in conjunction with the index register and is referred to as
"Zero Page, X" or "Zero Page, Y." The effective address is calculated by
adding the second byte to the contents of the index register. Since this is
a form of "Zero Page" addressing, the content of the second byte refer
ences a location in page zero. Additionally, due to the "Zero Page" ad
dressing nature of this mode, no carry is added to the high order eight
bits of memory and crossing of page boundaries does not occur.

Indexed Absolute Addressing—(X, Y indexing)—This form of addressing


is used in conjunction with X and Y index register and is referred to as
"Absolute, X," and "Absolute, Y." The effective address is formed by
adding the contents of X and Y to the address contained in the second
and third bytes of the instruction. This mode allows the index register to
contain the index or count value and the instruction to contain the base
address. This type of indexing allows any location referencing and the
index to modify multiple fields resulting in reduced coding and execution
time.

Implied Addressing—In the implied addressing mode, the address con


taining the operand is implicitly stated in the operation code of the instruc
tion.

Relative Addressing—Relative addressing is used only with branch in


structions and establishes a destination for the conditional branch.
The second byte of the instruction becomes the operand which is an
"Offset" added to the contents of the lower eight bits of the program counter
APPENDIX A 749

when the counter is set at the next instruction. The range of the offset is
-lEfl to +127 bytes from the next instruction.

Indexed Indirect Addressing—In indexed indirect addressing (referred to


as [Indirect, X]), the second byte of the instruction is added to the contents
of the X index register, discarding the carry. The result of this addition
points to a memory location on page zero whose contents are the low
order eight bits of the effective address. The next memory location in page
zero contains the high order eight bits of the effective address. Both mem
ory locations specifying the high and low order bytes of the effective ad
dress must be in page zero.

Indirect Indexed Addressing—In indirect indexed addressing (referred to


as [Indirect, Y]), the second byte of the instruction points to a memory
location in page zero. The contents of this memory location are added to
the contents of the Y index register, the result being the low order eight
bits of the effective address. The carry from this addition is added to the
contents of the next page zero memory location, the result being the high
order eight bits of the effective address.

Absolute Indirect—-The second byte of the instruction contains the low


order eight bits of a memory location. The high order eight bits of that
memory location is contained in the third byte of the instruction. The con
tents of the fully specified memory location are the low order byte of the
effective address. The next memory location contains the high order byte
of the effective address which is loaded into the sixteen bits of the program
counter.

Instruction Set—Alphabetic Sequence


ADC Add Memory to Accumulator with Carry
AND "AND" Memory with Accumulator

ASL Shift Left One Bit (Memory or Accumulator)

BCC Branch on Carry Clear


BCS Branch on Carry Set
BEQ Branch on Result Zero
BIT Test Bits in Memory with Accumulator
BMI Branch on Result Minus
BNE Branch on Result not Zero
BPL Branch on Result Plus
BRK Force Break
150 MACHINE LANGUAGE FOR COMMODORE MACHINES

BVC Branch on Overflow Clear


BVS Branch on Overflow Set

CLC Clear Carry Flag


CLD Clear Decimal Mode
CLI Clear Interrupt Disable Bit
CLV Clear Overflow Flag
CMP Compare Memory and Accumulator
CPX Compare Memory and Index X
CPY Compare Memory and Index Y

DEC Decrement Memory by One


DEX Decrement Index X by One
DEY Decrement Index Y by One

EOR "Exclusive-OR" Memory with Accumulator

INC Increment Memory by One


INX Increment Index X by One
INY Increment Index Y by One

JMP Jump to New Location


JSR Jump to New Location Saving Return Address

LDA Load Accumulator with Memory


LDX Load Index X with Memory
LDY Load Index Y with Memory
LSR Shift One Bit Right (Memory or Accumulator)

NOP No Operation

OR A "OR" Memory with Accumulator

PHA Push Accumulator on Stack


PHP Push Processor Status on Stack
PL A Pull Accumulator from Stack
PLP Pull Processor Status from Stack

ROL Rotate One Bit Left (Memory or Accumulator)


ROR Rotate One Bit Right (Memory or Accumulator)
RTI Return from Interrupt
APPENDIX A 757

RTS Return from Subroutine

SBC Subtract Memory from Accumulator with Borrow


SEC Set Carry Flag
SED Set Decimal Mode
SEI Set Interrupt Disable Status
STA Store Accumulator in Memory
STX Store Index X in Memory
STY Store Index Y in Memory

TAX Transfer Accumulator to Index X


TAY Transfer Accumulator to Index Y
TSX Transfer Stack Pointer to Index X
TXA Transfer Index X to Accumulator
TXS Transfer Index X to Stack Register
TYA Transfer Index Y to Accumulator

Programming Model
ACCUMULATOR A

INDEX REGISTER Y

INDEX REGISTER X
15 7 0
PCH PCL PROGRAM COUNTER "PC"
8 7

STACK POINTER "S"

NIV| | B| D| I | Z |c| PROCESSOR STATUS REG "P"

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

BCC BRANCH ON C = 0 (2)

BCS BRANCH ON C = 1 (2)


APPENDIX A

BEO BRANCH ON Z=1 (2)

BIT AAM

BMI BRANCH ON N = 1 (2)


i
BNE BRANCH ON Z = 0 (2)

BPL BRANCH ON N = 0 (2)

BRK (See Figure A-1)

BVC BRANCH ON V = 0 (2)

BVS BRANCH ON V=1 (2)

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

JMP JUMP TO NEW LOC.

(See Figure A-2)


fI fkCi "t w fK1Jt t
JSR JUMP SUB

| LDA
CO
CCCMM L5O to M ss
MUJo18LC
iZ
£
<
CO

O)
753

C8£s OCJM T»- O


CCM §o O CM
f«]•Dr■*CCCOOO8332f\slntcmnOOOJCCMMCMoQ.<^ £s8SCCMMT-OfC5Mit X CO|t
C8s CMM 8HIo
CO> Mf8t
CONDITION
Abs. Y RelallV(j Indiract Z,Pag«>, Y CODES
II4STRUCTIONS KliP Ahmsluite Zftro Pa go Acc urn lift1 (Inrt .)> (Irtd •)* Z, Pa qe, X Abei.X

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

PHA A-MS S - 1-»S 4R 1

PHP P->MS S - 1-*S OR 1

PLA S + 1-*S MS-»A 8R 4 1

(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

RTI RTRN INT

(See Figure A-1) 60 6 1

RTS RTRN SUB


0 0 «> 4 F9 3 i^i^(3)- - *
0 F5 F1 6 ? F1 ? F5 4 2 FD 3
SBC A - M - C-»A (1) E9 ED 4 3 3
0 1
SEC 1-»C 3fl
1 -
SED 1—D FR ? 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

(1) ADD 1 TO "N" IF PAGE BOUNDARY IS CROSSED. X INDEX X + ADD ^MODIFIED


(2) ADD 1 TO "N" IF BRANCH OCCURS TO SAME PAGE. Y INDEX Y - SUBTRACT - NOT MODIFIED
ADD 2 TO "N" IF BRANCH OCCURS TO DIFFERENT PAGE. A ACCUMULATOR A AND M7 MEMORY BIT 7
(3) CARRY NOT = BORROW. M MEMORY PER EFFECTIVE ADDRESS V OR M6 MEMORY BIT 6
(4) IF IN DECIMAL MODE Z FLAG IS INVALID Ms MEMORY PER STACK POINTER V EXCLUSIVE OR N NO. CYCLES
ACCUMULATOR MUST BE CHECKED FOR ZERO RESULT. # N0- BYTES

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:

*** COMMODORE BRSIC ***

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.

This early machine is becoming rare.

PET/CBM—Upgrade ROM
The first PET that can handle disk. It can be recognized by the message
seen at power up:

### COMMODORE BRSIC ###

using the numbers sign (or octothorpe, if you like).


This is much cleaner logic than the previous machine. Its internal structure
is similar to that of later PET/CBM units (the 4.0 machines), so that it has
much in common with them.

It does not have specialized disk commands such as CATALOG,


SCRATCH, or DLOAD (the 4.0 disk commands); but these are "conve
nience" commands and the Upgrade ROM unit can do everything that the
later units do.

Upgrade ROM machines have a BASIC annoyance: under some circum


stances, string variables need to be tidied up using a technique called
"garbage collection." This takes place automatically when needed; but
when it does, the machine will freeze and seemingly will be dead for a
period that may last from a few seconds to as long as a half hour or more.

PET/CBM—4.0 ROM and 80 Characters


This class of machine has been a mainstay of the Commodore line for
years. It may be recognized by the message seen at power up:

*** COMMODORE BASIC A .D ***

For the first time, a number appears in the message.


APPENDIX B 157

These machines are characterized by new BASIC disk commands


(CATALOG, etc.) and elimination of garbage-collection delays. Their in
ternal architecture, especially zero page, is quite similar to the previous
Upgrade ROM computers.

Some time after the initial production of 40-column machines, 80-column


machines were introduced, as well as a new 40-column version called the
"fat 40." The later machines are distinguished by new screen/keyboard
features, most noticeable of which is that the cursor movement keys repeat
automatically.

Subsequently, two memory-expanded machines became available. The


8096 came fitted with 96K of RAM; the extra 64K was "bank switched"
into memory as needed in blocks of 16K. The SuperPET, too, had an
extra 64K of RAM that was bank switched in 4K blocks; it also came with
an additional microprocessor (the 6809) used primarily for implementing
high-level languages. Both the 8096 and the SuperPET may be used as
conventional CBM 8032 computers; the extra memory may be ignored.

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.

BASIC reverted to Upgrade ROM style—no special disk commands and


potentially slow garbage collection. Other than that, BASIC was not trimmed.
All the functions and features remained, and some attractive new screen
editing features were added, such as automatic repeating keys.

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.

The VIC-20 is somewhat vexing for machine language programming work.


Depending on the amount of extra memory fitted (none, 3K, or 8K and
over), the location of start-of-BASIC and of screen memory will vary.

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

modore 64 comes with a 6510 microprocessor; addresses D and 1 are


reserved for "bank switching" of memory.

BASIC is identical to that of the VIC—no special disk commands and


potentially slow garbage collection. There's no built-in machine language
monitor, so one must be loaded. The SYS command, as with the VIC,
allows preloading of registers A, X, and Y if desired.

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.

The Commodore 64 is the first Commodore machine in which it is some


times desirable to write totally in machine language, with no BASIC at all.
BASIC can be swapped out to release extra RAM, and large applications
(word processors, spread sheets, and so on) are likely to do this.

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.

Color and sound are implemented differently to the Commodore 64.

There's a built-in machine language monitor with expanded features, such


as assemble and disassemble. This one is convenient for machine lan
guage programmers.

The memory arrangement is more sophisticated than on previous ma


chines; large implementations may require insight into the machine's de
tailed architecture.

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.

The cassette buffer is no longer at $D33D, so that the examples given


in this book will need to be moved to a new part of RAM (addresses
$D4DD to $D7FF are available). Bank switching is more complex than
on other models. Beginners will find that there are more things to be kept
APPENDIX B 759

track of in this machine. If possible, beginners should try to find a simpler


computer on which to take their first steps.

Implementation of large-scale programs require setting up a "transfer se


quence" program to link the program's memory bank to that of the kernal.
Usually, a bootstrap program will be needed to set everything up.

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.

1. In C64 mode, it is identical in almost all aspects to the Commodore 64. As


such, the machine in this mode has access to only 64K of memory, and
normally uses only standard Commodore 64 peripherals and screen formats.
2. In C128 mode, it is an extended version of the 64 with extra features: 128K
of memory (arranged in two banks of 64K per bank); the possibility of using
an 80-column screen; the possibility of interfacing a disk unit that will operate
at a much higher speed than that of the Commodore 64.
The C128 has extensive hardware compatibility with the Commodore 64.
The 64's standard disk and printer can be hooked up in the usual way, but
with no speed improvement. Sound and 40-column graphics may be achieved
with POKEs to the same memory locations as for the 64.
The processor used for the C64 and C128 is the 8500, which has the same
instruction set as other machines such as the Commodore 64. Machine
language software is not generally upwardly compatible with the Commodore
64 because of differing RAM usage between the two machines.
3. CP/M mode uses the Z80-A microprocessor, whose machine language in
struction set is completely different from that of the 650x. These are outside
the scope of this book.

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.

The Commodore 128 comes with a 8501 microprocessor. As in the Com


modore 64, addresses D and 1 are reserved for control ports.

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.

The term "bank" is misleading; the word "configuration" might be more


appropriate. For example, when a programmer calls for BANK 13 the
APPENDIX B — COMMODORE 128 161

computer supplies a configuration which is partly RAM and partly ROM. A


call for BANK 15 would invoke a different configuration of RAM and ROM.
Some of the RAM and ROM are the same as in the previous configuration.
The details are not important at this stage, but a drawing of some of the
popular "bank" configurations might be helpful.

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.

Choosing a Bank for Your Program (128)


You can move from one bank to another, but it takes extra work. It's best,
when you can, to pick an initial bank configuration that you can live with
during the running of your program. A quick rundown of the most popular
configurations follows. Choose Bank 15 if you can.

First, a general rule: Addresses D and 1 are reserved, and so are ad


dresses hexadecimal FFDD to FFD4. These addresses don't "bank" and
are always there. The 251 addresses above $FFD4 are bank switched,
but are seldom useful to the average programmer. In a standard C128,
addresses from $3to$D3FF always refer to RAM Bank 0 regardless of
the bank selected.

Banks 0, 1, 2, and 3 are pure RAM—no ROM to help you do things, no


I/O chips to help you input and output data. Banks 2 and 3 are reserved
162 MACHINE LANGUAGE FOR COMMODORE MACHINES

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

C—Character generator ROM

"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 4, 5, 6, and 7 are similar to Banks 0-3 below address $flDDD. A


set of ROM lies over the RAM at addresses $flDDD to $FFFF, except
for addresses $DDDD to $DFFF which contain I/O chip registers. This
ROM is internal, which means that it can be plugged into spare sockets
within the C128. Unless you plan to make your own ROM-like chips, in
cluding your own Kernal program, stay away from these.

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.

Banks 12 and 13 are similar to Bank 0 below address $flDDD. A set of


ROM (internal for Bank 12, external for Bank 13) lies over the RAM at
addresses $flDDD to $BFFF, and the standard Kernal ROM lies over
the RAM at addresses $CDDDto$FFFF, except for addresses $DDDD
to $DFFF which contain I/O chip registers. These look good for the
average application if you don't need BASIC. You'll get lots of RAM memory
to play with, yet the I/O chips and Kernal are there and available to you.

Banks 14 and 15 are similar to Bank 0 below address $4DDD. System


ROM (Basic and Kernal) lies over the RAM at addresses $4UU0 to $FFFF,
except for addresses $DDDD to $DFFF which contain the character
generator ROM (Bank 14) or I/O chip registers (Bank 15). These are the
easiest to use, especially Bank 15 with free access to I/O. The only
limitation is the more limited access to RAM for your program. Since you
still have over 12K of RAM to play with, that shouldn't be a major problem.

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

Data in Other Banks (128)


Wherever your program ends up, you may find a need to reference data
in other banks—to load it, store it, or compare it. Three Kernal subroutines
are available to help you do these actions. They are:

Action Name Address


Load INDFET $FF74
Store INDSTA $FF7?
Compare INDCMP $FF?A

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.

The indirect address information is passed to the subroutine in one of


several ways:

For INDFET, load the address to register A;


For INDSTA, put the address into location SDEBR;
For INDCMP, put the address into location $DECfl.

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.

Jumping to Other Banks (C128)


A JMP is slightly easier than a JS R, but neither is hard. The call addresses
are:

Action Name Address Alternate

JMP JMPFAR $FF?1 $DEE3


JSR JSRFAR $FFbE $DECD

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

memory: A at t, X at 7, and Y at 6. Remember, you must set up addresses


2 to 4 before making the call.

The same setup applies to both JMPFARandJSRFAR. About the al


ternate address: you cannot JMP or JSR to $FF71 or $FFbE if the
ROM isn't there—in which case you must use the alternate address in
low memory which is never switched. When you have everything set up,
you may JMP to JMPFAR, since you will not need to come back. You
must JSR to JSRFAR, and it's worth noting that this call will normally
return to Bank 15 only. If you want to look at registers after the return,
they will be saved in the locations noted above.

The Screen (C128)


The 40-column screen is mapped in the "usual" way. That is, whatever
characters are stored in screen memory (usually $D4DDto$D7E7) will
be seen on the screen, and whatever appears on the nongraphics screen
may be inspected at the corresponding point within memory. Material
dealing with how to use the 64's video chip will normally be valid for the
128.

The 80-column screen is driven in an entirely different manner. The char


acters on screen are mapped from memory—but not the computer's main
memory. Instead, the video controller uses a "private" memory. We have
to do a moderate amount of work to inspect or change this memory; a
minimum of six commands are usually needed to reference a screen
memory byte. For an illustration of the cumbersome method needed to
do this, look at the character stored in the second position of the top row
of the 80-column screen. The internal memory address of this character
is DDD1 (high byte D, low byte 1); here we go. POKE 547 64, 16 : POKE
547 64 , D to set the high address byte. POKE 547 64 ,iq : POKE
54764,1 to set the low address byte. Finally, POKE
54764,31:PRINT PEEK(54765). We'll finally get the character
(in screen code, not ASCII) ... but that's a lot of work compared to a
single PEEK on 40 columns.

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.

When you see a reference to a POKE or PEEK location—in this book or


from other sources—check it in these maps. They will help add perspec
tive.

767
768 MACHINE LANGUAGE FOR COMMODORE MACHINES

"Original ROM" PET


The Great Zero-Page Hunt
Most users help themselves to the high part of the input buffer ($004 0
to $ D D 5 q, which is not used except when long lines of data are inputted.

Most zero-page locations may be copied to another part of memory so


that their original contents can be restored after use. However, the pro
grammer should take great care in modifying the following locations, which
are critical within the operating system or BASIC: $03, $05, $E>4 to
$t7, $7fi to $fl?, $flR, $&2 to $&3, $B7, $C2 to $Dq, $E0 to $EZ,
$F5.

Memory Map
Hex Decimal Description

0000-000B 0-2 USE jump


0003 3 Current I/O -prompt
suppress

0005 5 Cursor control position


oooa-oooq a-q Integer value (for SYS, GOTO,
and so on)
OOOft-OOSq iD-aq Input buffer
005ft qo Search character
005B qi Scan-between-quotes flag
D05C qa Input buffer pointer; number of
subscripts
005D q3 Default DIM flag
005E q^i Type: FF= string;
00 = numeric
005F qs Type: 80 = integer; 00=floating
point
ooto qt Flag: DATA scan; LIST quote;
memory

00E.1 q? Subscript flag; FNX flag


00t>2 qa 0=INPUT; $40=GET;
$qa=RERD
00t3 qq RTN sign/comparison
evaluation flag
00b4 100 Input flag (suppress output)
OObS-OOfc? 1D1-1D3 Pointers for descriptor stack
APPENDIX C — "ORIGINAL ROM" PET 769

Hex Decimal Description

DDta-DD7D 104-113 Descriptor stack (temporary


strings)
DD71-0074 113-llt Utility pointer area
DD75-DD7a 117-130 Product area for multiplication
DD7A-DD7B 133-133 Pointer: start-of-BASIC
DD7C-DD7D 134-135 Pointer: start-of-variables
D07E-DD7F 13k-13? Pointer: start-of-arrays
oaaD-ooai iEa-iEq Pointer: end-of-arrays
DDaE-DDa3 13D-131 Pointer: string-storage (moving
down)
DDa<-DDa5 133-133 Utility string pointer
ooab-QDa7 134-135 Pointer: limit-of-memory
oaaa-DDaq 13k-137 Current BASIC line number
DDBA-DDaB 13S-13S Previous BASIC line number
DDac-DoaD Jj £-\ LJ ~~" \f ^-\ \\ PointerBASIC statement for
CONT
DQaE-DOaF Jj M C "~~ At M f Current DATA line number
DoqD-ODqi 144-145 Current DATA address
0DqE-D0q3 JJ ^~i O ^~ Jj M 1 Input vector
DDq^-DDqs Jj +-\ U ~~ JJ ^ 1 Current variable name
DDqt-DDq? 1SD-151 Current variable address
Doqa-DDqq 155-153 Variable pointer for FOR/NEXT
ooqA-ooqB 154-155 Y-save; op-save; BASIC
pointer save
DDqc 15b Comparison symbol
accumulator
DDqD-DDAE 157-lbE Miscellaneous work area,
pointers, and so on
DDA3-DDA5 It3-lt5 Jump vector for functions
DDAb-DDAF lbb-175 Miscellaneous numeric work
area

DDBO 17k Accum#1: exponent


DDB1-DDB4 177-iaD Accum#1: mantissa
DDB5 iai Accum#1 :sign
DDBb Ifl3 Series evaluation constant
pointer
DDB7 103 Accum#1 hi-order (overflow)
DDBa-DDBD Accum#2: exponent, and so on
DDBE IRQ Sign comparison, Acc#1 versus
#2
DDBF iqi Accum#1 lo-order (rounding)
170 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DDCD-DDC1 112-113 Cassette buffer length/series


pointer
DDCE-DDDR 114-217 CHRGET subroutine; get
BASIC character
DDCR-DDCfi ED1-EDE BASIC pointer (within
subroutine)
DODA-DDDE Elfl-522 Random number seed
ODED-ODE1 Pointer to screen line
DDEE EEb Position of cursor on above line
DDE3-D0E4 ES7-E5fl Utility pointer: tape, scroll
0DE5-DDEb EEq-E3D Tape end address/end of
current program
DDE7-DDEQ E31-E3E Tape timing constants
DOEI E33 Tape buffer character
DDEA B3A Direct/programmed cursor:
D= direct
DDEB E35 Tape read timer 1 enabled
DDEC 23b EOT received from tape
DDED E37 Read character error
DDEE E3fl Number of characters in file
name

DDEF E3R Current file logical address


DDFD EAO Current file secondary address
DDF1 241 Current file device number
OOFE Line margin
D0F3-DDF4 2A3-EAA Pointer: start of tape buffer
DDF5 S45 Line where cursor lives
DDFt E4fc Last key/checksum/
miscellaneous
DDF7-DDFS E47-E4fl Tape start address
OOFq-OOFR E4R-E5D File name pointer
DDFB E51 Number of INSERTS
outstanding
DDFC Write shift word/read character
in
DDFD E53 Tape blocks remaining to write/
read
DDFE E54 Serial word buffer
D1DD-D1D& 25t-2tt STR$ work area
D1D0-D13E 25t-31fl Tape read error log
D1DD-D1FF 2St-511 Processor stack
APPENDIX C — "ORIGINAL ROM" PET 171

Hex Decimal Description

DEOO-OEDE 51E-513 Jiffy clock for TI and TI$


0E03 515 Which key down: 255 = no key
0ED4 Sit Shift key: 1 if depressed
0E05-0EDt 517-51fl Correction clock
0ED7-DE0fl 5iq-5ED Cassette status, #1 and #2
OEDH 5E1 Keyswitch PI A: STOP and
RVS flags
OEDA 5EE Timing constant for tape
OEDB 5E3 Load = 0; verify =1
DEDC SBA Status word ST
QEDD 5E5 Number of characters in
keyboard buffer
DEDE 5Eb Screen reverse flag
DEDF-QElfl 5E?-53b Keyboard input buffer

OElS-CTElft 537-53fl IRQ vector


DE1B-DE1C 53^-540 BRK interrupt vector
DE1D 541 IEEE output: 25 5 = character
pending
DE1E 5<E End-of-line-for-input pointer
OEEO-OEE1 544-545 Cursor log (row, column)
DEEE 54t IEEE output buffer
DEE3 547 Key image
0EE4 54fl D= flash cursor
DEES 54R Cursor timing countdown
OEEt 55D Character under cursor
DEE? 551 Cursor in blink phase
DEEfl 55E EOT received from tape
QEEq-DE41 553-577 Screen line wrap table
QE4E-QE4B 57fl-5fi7 File logical address table
0E4C-DESS saa-sq? File device number table
QE5t-0E5F 5qa-bD7 File secondary address table
DEtD bDfl Input from screen/from
keyboard
DEtl bDq X save
OEtE blO How many open files
DEt3 bll Input device, normally D
DEt4 tlE Output CMD device, normally 3
DELS bl3 Tape character parity
DEtt fcl4 Byte-received flag
DEtfl-OEbH bl5-t.lt File name pointer; counter
172 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DEbC tan Serial bit count


DEbF tE3 Cycle counter

DE7D bE4 Tape writer countdown


DE71-DE7E fcE5-bEfc Tape buffer pointers, #1 and
#E
DE73 ta? Write leader count; read pass
1/2
DE74 bEfl Write new byte; read error flag
DE75 fcER Write start bit; read bit
sequence error

DE7b-DE77 t3D-fc31 Error log pointers, pass 1/2


DE7fl b3E D= scan/1-15= count/
$ A D = load/$ fl D = end
DS7R t33 Write leader length; read
checksum
DE7A-D33C1 fc3^i-flE5 Tape#l input buffer
D33A-03FS flEt-lD17 Tape#E input buffer
D3FA-D3FB IDlfl-lDIR Monitor extension vector
D4DD-7FFF lDE4-3E7b7 Available RAM including
expansion
flDDD-fi3E7 3E7tfl-337b7 Screen RAM memory
CDDD-E7Ffl z;qi5E-5c13flzi BASIC ROM; part of kernal
ROM
EfllD-Efil3 PI A 1 (6520)-keyboard
interface
EflED-EflE3 ER^E^i-ER^E? PI A S (6520)-IEEE interface
Efi4D-Efl4F 5cK5t--5cl471 VIA (6522)-Miscellaneous
interface, timers
■pnnn-FFFP fn!44 D —15535 Kernal ROM routines.

PI A and VIA charts are the same as shown for Upgrade/4.0 units.

UPGRADE and BASIC 4.0 Systems


The Great Zero-Page Hunt
Zero-page locations are tough to find in these areas. Locations $1F to
$E7, $4Bto$50, and $54 to $5D are work areas available for tem
porary use. If tape is not being read or written, addresses $B1 to $C3
are available.
APPENDIX C — UPGRADE AND BASIC 4.0 SYSTEMS 173

Most zero-page locations may be copied to another part of memory so


that their original contents can be restored after use. The programmer
should take great care, however, in modifying the following locations, which
are critical withjn the operating system of BASIC: $1D, $13 to $15,
$Eflto$3 5, $37, $5Dto$51, $b5, $7Dto$fl7, $flDto$BD,
to $FA.

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.

Hex Decimal Description


ODDD-DDDE D-E USR jump
DDD3 3 Search character
OQUA A Scan-between-quotes flag
0OD5 5 Input buffer pointer; number of
subscripts
DDDb b Default DIM flag
DDD7 7 Type: FF = string; DD = numeric
DDOfl a Type: 00 = integer;
DD=floating point
Flag: DATA scan; LIST quote;
memory
OQDft ID Subscript flag; FNX flag
DDDB 11 D=INPUT; $4D=GET;
$qfl=READ
DDDC IE ATN sign/comparison
evaluation flag
DDDD-DDDF 13-15 *Disk status DS$ descriptor
DD1O ^Current I/O device for prompt-
suppress
DD11-DD1E 17-lfl Integer value (for SYS, GOTO,
and so on)
DD13-DD15 iq-Ei Pointers for descriptor stack
DDlt-DDlE EE-3D Descriptor stack (temporary
strings)
DD1F-DDEE 31-3^ Utility pointer area
0DE3-D0E7 35-3^ Product area for multiplication
DDES-ODER AD-Al Pointer: start-of-BASIC
OOE&-DQEB AE-A3 Pointer: start-of-variables
DDSC-DDED AA-AS Pointer: start-of-arrays
174 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

005E-005F 4b-47 Pointer: end-of-arrays


DD3D-DD31 4B-4q Pointer: string-storage (moving
down)
DD3E-DD33 50-51 Utility string pointer
0034-0035 55-53 Pointer: limit-of-memory
0D3b-0037 54-55 Current BASIC line number
0033-003=1 5fc-57 Previous BASIC line number
003R-003B 5fl-5R Pointer: BASIC statement for
CONT
003C-003D fcO-tl Current DflT& line number
003E-003F fc5-b3 Current DATS address
0040-0041 fc4-t5 Input vector
0045-0043 fcfc-fc7 Current variable name
004b-0047 70-71 Variable pointer for FOR/NEXT
004fl-004q 75-73 Y-save; op-save; BASIC
pointer save
004A 74 Comparison symbol
accumulator
004B-0050 75-fiD Miscellaneous work area,
pointers, and so on
0051-0053 fll-fl3 Jump vector for functions
0054-005D fl4-R3 Miscellaneous numeric work
area

005E ^4 Accum#1: exponent


00SF-00b5 qs-qa Accum#1: mantissa
D0fc3 qq Accum#1: sign
00b4 100 Series evaluation constant
pointer
00L5 101 Accum#1 hi-order (overflow)
DDtt-DDtB 105-107 Accum#2: exponent, and so on
ootc loa Sign comparison, Acc#1 versus
#2
OObD lot Accum#1 lo-order (rounding)
OObE-OObF 110-111 Cassette buffer length/series
pointer
0070-0057 115-135 CHRGET subroutine; get
BASIC character
0077-0073 iiq-150 BASIC pointer (within
subroutine)
OOflfl-OOflC 13fc-140 Random number seed
OOflD-OOflF 141-143 Jiffy clock for TI and TI$
APPENDIX C — UPGRADE AND BASIC 4.0 SYSTEMS 775

Hex Decimal Description

DQqD-ooqi IRQ vector


Doqa-QDqa BRK interrupt vector
aoq^-oaqs 1A&-1A1* NMI interrupt vector
DDSt 15D Status word ST
DDq? 151 Which key down: 255 = no key
oaqa 155 Shift key: 1 if depressed
Daqq-Doqa 153-154 Correction clock
DDRB 155 Keyswitch PIfi: STOP and
RVS flags
aoqc 15fc Timing constant for tape
ODRD 157 Load = D; verify =1
DDRE 15fl Number of characters in
keyboard buffer
DDqF isq Screen reverse flag
DDAD ifcD IEEE output: E55 = character
pending
QOfll Ibl End-of-line-for-input pointer
0DR3-D0A4 It3-lt4 Cursor log (row, column)
DD&5 It5 IEEE output buffer
DQAb Key image
DOfi? It? D= flash cursor
DOflfl Ibfl Cursor timing countdown
DDfiq itq Character under cursor
DDfifi 170 Cursor in blink phase
DDAB 171 EOT received from tape
DDfiC 17a Input from screen/from
keyboard
ODAD 173 X save
DOAE 174 How many open files
DDfiF 175 Input device, normally D
DDBD 17b Output CMD device, normally 3
DDB1 177 Tape character parity
0DB2 17fl Byte-received flag
DDB3 i?q Logical address temporary save
DDB4 iflD Tape buffer character; MLM
command
DDB5 Ifll File name pointer; MLM flag;
counter
D0B7 Ifl3 Serial bit count
ODBq Ifl5 Cycle counter
ODBfi Iflfc Tape writer countdown
776 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DDBB-DDBC Ifl7-lflfi Tape buffer pointers, #1 and


#a
DOBD 1QR Write leader count; read pass
1/2
DDBE IRQ Write new byte; read error flag
DDBF 1R1 Write start bit; read bit
sequence error

DDCD-DDC1 1R2-1R3 Error log pointers, pass 1/2


DOCE 1R4 D = scan/1 -15= count/
$4D = load/$flD = end
DDC3 1R5 Write leader length; read
checksum
0DC4-D0C5 IRt-lR? Pointer to screen line
DOCb IRQ Position of cursor on above line
DDC7-0DCfi ISR-EDD Utility pointer: tape, scroll
OOCR-DOCA ED1-EDE Tape end address/end of
current program
DDCB-DDCC S03-ED4 Tape timing constants
DDCD ED5 D = direct cursor; else
programmed
DDCE EOb Tape read timer 1 enabled
DDCF ED? EOT received from tape
DODD EDfl Read character error

DDD1 EDR Number of characters in file


name

ODDS SID Current file logical address


DDD3 Ell Current file secondary address
D0D4 E1E Current file device number
DDD5 E13 Right-hand window or line
margin
DDDb-DDD? 514-515 Pointer: start of tape buffer
DDDfl Elk Line'where cursor lives
DDDR E17 Last key/checksum/
miscellaneous
DODft-DDDB Slfl-SIR File name pointer
DDDC EED Number of INSERTS
outstanding
DDDD EE1 Write shift word/read character
in
DDDE SEE Tape blocks remaining to write/
read
APPENDIX C — UPGRADE AND BASIC 4.0 SYSTEMS 177

Hex Decimal Description

DDDF 223 Serial word buffer


DDED-DDFfl 224-24S (40-column) Screen line wrap
table
DDED-DDE1 224-225 *(80-column) Top, bottom of
window
00E2 22b *(80-column) Left window
margin
0QE3 227 *(80-column) Limit of keyboard
buffer
00E4 22fi *(80-column) Key repeat flag
DDES 22R *(80-column) Repeat countdown
DDEfc 23D *(80-column) New key marker
DDE? 231 *(80-column) Chime time
DDES 232 *(80-column) HOME count
ODER-DOER 233-234 *(80-column) Input vector
DDEB-DDEC 235-23fc *(80-column) Output vector
OOFS-OOFR 24R-250 Cassette status, #1 and #5
DDFB-DDFC 251-252 MLM pointer/tape start address
DDFD-DDFE 253-254 MLM/ DOS pointer,
miscellaneous
0100-010R 25b-2bb STR$ work area, MLM work
D1DD-D13E 25b-31fi Tape read error log
D1DD-D1FF 25b-511 Processor stack
0200-0250 512-5R2 MLM work area; input buffer
0251-025R 5R3-tD2 File logical address table
02SB-02b4 E.D3-bl2 File device number table
02b5-02kE bl3-fc22 File secondary address table
02bF-027fl E.23-b32 Keyboard input buffer
027ft-033cl fc34-S25 Tape#l input buffer
033A-03Fcl fl2fc-1017 Tape#2 input buffer
D33fi-D3fiO fl2t-flRfc *DOS work area
D3ECI 10D1 (Fat 40) New key marker
03EA 1D02 (Fat 40) Key repeat countdown
D3EB 1003 (Fat 40) Keyboard buffer limit
D3EC 1004 (Fat 40) Chime time
D3ED 1D05 (Fat 40) Decisecond timer
D3EE 100b (Fat 40) Key repeat flag
03EE-D3F7 100b-1015 (80-column) Tab stop table
03EF 10D7 (Fat 40) Tab work value
03FD-q lDOfi-1017 (Fat 40) Tab stops
D3FR-03FB loia-ioiq Monitor extension vector
778 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

D3FC 1D2Q *IEEE timeout defeat


D40D-7FFF Available RAM including
expansion
flODD-fl3E7 357tfl-337b7 (40-column) Video RAM
flDDD-fl7CF 3E7bfl-347b7 *(80-column) Video RAM
qQDD-aFFF 3bflfc4-45D55 Available ROM expansion area
BDDD-E7FF 45DSb-5q3qi BASIC ROM, part of kernal
EfllD-Efll3 PI A 1-keyboard I/O
Efl5D-EflE3 PI A 2-IEEE-488 I/O
Efl4D-Efi4F VIA-I/O and timers
EflflD-Eflfll 5q55D-E (80-column and Fat 40) CRT
controller
FDDD-FFFF Kernal ROM

6520

Diag Sens/ Tape Switch Sense


E810 EOlin Keyboard Row Select 59408
Uncrash #1 #2

Tape#1 In (Screen Blank—Orig DDRA Tape#1 Input L


E811 59409
Latch ROM) EOI Out Access Control

E812 Keyboard Input for selected row 59410

Retrace Cassette#1 Motor DDRB Retrace Interrupt


E813 59411
Latch Output Access Control

Figure C.1
PIA1 chart

6520

E820 IEEE-488 Input 59424

DDRA
E821 ATN Int NDAC Out ATN Int Control 59425
Access

E822 IEEE-488 Output 59426

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

Retrace Tape#2 Tape NRFD


E840 DAVIn NRFD In ATNOu NDAC In 59456
In Motor Output Out

E841 Unused (See E84F) 59457

E842 Data Direction Reg ster B (for E840) 59458

E843 Data Direction Register A (for E84F) 59459

E844 Timer 1 59460

E845 59461

E846 Timer 1 Latch 59462

E847 59463

E848 Timer 2 59464

E849 59465

E84A Shift Register (unused) 59466

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

Irq Stats Timer Timer CB1 CB2 CA1 CA2


E84D SR 59469
1 2 Tape#2 (PUB) (PUP) G/TMode
E84E Unused 59470
Int Enabl Int Int Int Int Int unused..

E846F Parallel User Port Data Register PA 59471

Figure C.3
VIA chart

CBM 8032 and FAT-40


6545 CRT Controller
NOTES: 1. Registers are write-only.
2. Avoid extreme changes in Register 0. CRT damage could
result.
3. Register 0 will adjust scan to allow interfacing to external
monitor.
4. Register 12, Bit 4, will "invert" the video signal.
5. Register 12, Bit 5, switches to an alternate character set. The
character set is not implemented on most machines except
Super-PET.
780 MACHINE LANGUAGE FOR COMMODORE MACHINES

$E880 $E881 TYPICAL VALUES


59521 (DECIMAL)
59520
i TEXT GRAPHICS

0 HORIZONTAL TOTAL 49 49

1 HOR. CHAR. DISPLAYED 40 40

2 H. SYNC POSITION 41 41

3 v SYNC WIDTH H 15 15

4 XI VERTICAL TOTAL 32 40

5 ^X^| VERT. TOT. ADJUST 3 5

6 x VERTICAL DISPLAYED 25 25

7 x VERT. SYNC POSITION 29 33

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

NOTES: 1. REGISTERS ARE WRITE-ONLY


2. AVOID EXTREME CHANGES IN
REGISTER, OR CRT DAMAGE
COULD RESULT
3. REGISTER 0 WILL ADJUST SCAN
TO ALLOW INTERFACING TO
EXTERNAL MONITOR
4. REGISTER 12, BIT 4, WILL "INVERT"
THE VIDEO SIGNAL.
5. REGISTER 12, BIT 5, SWITCHES TO
AN ALTERNATE CHARACTER SET.
THE CHARACTER SET IS NOT
IMPLEMENTED ON MOST MACHINES
EXCEPT SUPER-PET.
Figure C.4
APPENDIX C — VIC 20 181

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.

Most zero-page locations may be copied to another part of memory so


that their original contents can be restored after use. However, the pro
grammer should take great care in modifying the following locations, which
are critical within the operating system or BASIC: $13, $lb to $lfl,
$EBto$3fl, $3 A, $53to$54,$kfl, $73 to $fiA, $RD to $RA, $AD
to $AE, $Bfi to $BA, $C5 to $FA.

Memory Map
Hex Decimal Description

DDDD-OODE D-E USE jump


DD03-DQD4 3-4 Float-fixed vector
Q005-D00E. 5-b Fixed-float vector
DDD7 7 Search character
DQDfi a Scan-quotes flag
DDDR q TAB column save
DODA ID D=L0AD,l=VERIFY
DDDB ii Input buffer pointer/number of
subscripts
DDDC IE Default DIM flag
DDDD 13 Type: FF = string; DD = numeric
DDDE IA Type: flD = integer;
DD=floating point
DDDF 15 DATA scan/LIST quote/
memory flag
0D1D 1b Subscript/FNx flag
DD11 17 0=INPUT;$40=GET;
$qa=READ
DD1E ia ATN sign/Comparison
evaluation flag
DD13 iq Current I/O prompt flag
DD14-0D15 ED-El Integer value
DDlfc BE Pointer: temporary string stack
DD17-0Dia 23-EA Last temporary string vector
182 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DDl^-DDEl E5-33 Stack for temporary strings


Q0EE-DDE5 34-37 Utility pointer area
DDEb-QQEA 3fl-4E Product area for multiplication
DDEB-DDEC 43-44 Pointer: start-of-BASIC
DDED-DDEE 45-4b Pointer: start-of-variables
DDEF-DD3D 47-4fl Pointer: start-of-arrays
DD31-DD3E 4CI-5D Pointer: end-of-arrays
DD33-DD34 51-5E Pointer: string-storage (moving
down)
0035-DD3t 53-54 Utility string pointer
DD37-DD3fl 55-5t Pointer: limit-of-memory
DDSR-DDSR 57-5fl Current BASIC line number
DD3B-DD3C 5R-tD Previous BASIC line number
DD3D-DD3E ti-ta Pointer: BASIC statement for
CO NT
DD3F-DD4D b3-b4 Current DATA line number
DD41-QD4E b5-bb Current DATA address
D043-DD44 b7-bfl Input vector
DD45-DD4t bH-7D Current variable name
DD47-DD4fl 71-7E Current variable address
DD4cl-DD4fi 73-74 Variable pointer for FOR/NEXT
DD4B-DD4C 75-7t Y-save; op-save; BASIC
pointer save
DD4D 77 Comparison symbol
accumulator
DD4E-DD53 7fl-fl3 Miscellaneous work area,
pointers, and so on
DD54-DD5t fl4-flb Jump vector for functions
0057-DQbQ fl7-qt Miscellaneous numeric work
area

OQtl Accum#l: exponent


DOLE-DDtS qa-iDi Accum#l: mantissa
QDbb IDE Accum#l: sign
DQL7 103 Series evaluation constant
pointer
DDbfl 1D4 Accum#1 hi-order (overflow)
DOtq-DDfcE 1D5-11D Accum#2: exponent, and so on
DDtF 111 Sign comparison, Acc#l versus
#5
DD7D Accum#l lo-order (rounding)
APPENDIX C — VIC 20 183

Hex Decimal Description

DD71-DD7E 113-114 Cassette buffer length/series


pointer
QQ73-0DfiA 115-130 CHRGET subroutine; get
BASIC character
DD7A-DD7B 1BB-1B3 BASIC pointer (within
subroutine)
DDfiB-DDflF 13=1-143 END seed value
DDSO 144 Status word ST
DDHl 145 Keyswitch PI A: STOP and
RVS flags
Timing constant for tape
DDR3 147 Load = D; verify =1
00^4 14fl Serial output: deferred character
flag
00^5 14R Serial deferred character
DDRt 15D Tape EOT received
00^7 151 Register save
DDRfl 155 How many open files
DDR^ 153 Input device, normally D
DORA 154 Output CMD device, normally 3
DDSB 155 Tape character parity
Doqc 15t Byte-received flag
DDRD 157 Direct = $ fl D/RUN = D output
control
DDqE 15fl Tape pass 1 error log/character
buffer
DD^F 15R Tape pass E error log corrected
DDAD-0DA5 ltD-ltB Jiffy Clock HML
DDA3 It3 Serial bitcount/EOI flag
DDA4 It4 Cycle count
DDA5 Ifc5 Countdown, tape write/bit count
DDAfc Itb Tape buffer pointer
DDA7 It? Tape write leader count/read
pass/inbit
DDAfl ita Tape write new byte/read error/
inbit count
DDAR itq Write start bit/read bit error/stbit
OOAA 17D Tape Scan;Cnt;Load;End/byte
assembly
DOAB 171 Write lead length/read
checksum/parity
184 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

ODRC-DDAD 172-173 Pointer: tape buffer, scrolling


DDftE-DDAF 174-175 Tape end address/end of
program

DDBD-DOB1 17E.-177 Tape timing constants


DDB5-00B3 17B-17R Pointer: start of tape buffer
00B4 1QD 1 =tape timer enabled; bit count
00B5 Ifll Tape EOT/RS232 next bit to
send
DDBb ifiE Read character error/outbyte
buffer
DDB? 103 Number of characters in file
name

DDBfl Current logical file


DDBq Ifl5 Current secondary address
ODBR Iflb Current device
DDBB-DDBC Ifi7-lfifl Pointer to file name
DDBD iaq Write shift word/read input
char
DDBE iqD Number of blocks remaining to
write/read
OQBF iqi Serial word buffer
DDCD iqa Tape motor interlock
00C1-00C2 I/O start address
DQC3-DDC4 1R5-1SE. Kernal setup pointer
QDC5 1R7 Last key pressed
DDCt isa Number of characters in
keyboard buffer
DDC7 iqq Screen reverse flag
DDCfi aoD End-of-line for input pointer
DDCq-DDCR 5D1-EDE Input cursor log (row, column)
DOCB ED3 Which key: b4 if no key
DDCC 2U4 D= flash cursor
DDCD 205 Cursor timing countdown
DDCE ZDt, Character under cursor
DDCF 5D7 Cursor in blink phase
DDDD EDfi Input from screen/from
keyboard
DDD1-DDDE SDS-EID Pointer to screen line
DDD3 Ell Position of cursor on above line
APPENDIX C — VIC 20 185

Hex Decimal Descr/pfton

D0D4 E1E D = direct cursor; else


programmed
DDD5 E13 Current screen line length
DDDfc Row where cursor lives
ODD? E15 Last inkey/checks urn/buffer
DDDfl ait Number of INSERTS
outstanding
QODR-OOFO E17-E4D Screen link table
DDF1 341 Dummy screen link
ODFS E4E Screen row marker
DDF3-00F4 E43-E44 Screen color pointer
DDF5-DDFfc E45-E4t Keyboard pointer
DDF7-DDFfl E47-E46 RS-232 Rev pntr
DOFR-ODFA E^q-ESD RS-232 Tx pntr
DDFF-DlDfi ESfc-Efct Floating to ASCII work area
D100-1D3E E5b-31fl Tape error log
D1D0-D1FF 35E.-511 Processor stack area
QEQD-0E5O 5ia-tDD BASIC input buffer
DE5q-DEbE tOl-tlD Logical file table
QEb3-QEbC tn-tao Device number table
DEbD-D£7b tai-t3D Secondary address table
DE77-QEflD t31-t4D Keyboard buffer
OEfil-OEflE t41-t43 Start of BASIC memory
D£fl3-0Efi4 t»<3-fc^4 Top of BASIC memory
DEfl5 Serial bus timeout flag
DEflb Current color code
DEfl? fc47 Color under cursor
DEflfl Screen memory page
DEflR Maximum size of keyboard
buffer
OEfifl fc5D Repeat all keys
DEflB t>51 Repeat speed counter
DEflC tsa Repeat delay counter
DEflD fc53 Keyboard shift/control flag
DEflE Last shift pattern
DEflF-OERD fc55-fc5t Keyboard table setup pointer
DE^l fc57 Keymode (Kattacanna)
DESE fc5fl D = scroll enable
DEH3 fc5R RS-232 chip control
DEq< bfcD RS-232 chip command
786 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Descr;pf;on

osqs-OERb ttl-ttE Bit timing


OER? RS-232 status

DERfl Number of bits to send


oe^-derb RS-232 speed/code
DERB bt7 RS-232 receive pointer
OERC btfl RS-232 input pointer
DERD ttq RS-232 transmit pointer
QESE t7D RS-232 output pointer
DERF-OEaD t71-b7E IRQ save during tape I/O
D3Q0-0301 7bfl-7tS Error message link
D3QE-D303 770-771 BASIC warm start link
03D4-D305 77E-773 Crunch BASIC tokens link
D3Qb-0307 774-775 Print tokens link
D3Dfl-030R 77b-777 Start new BASIC code link
D3Da-D3DB 77fl-77R Get arithmetic element link
D3DC 7flO SYS a-reg save
D3DD 7fll SYS X-reg save
D3DE 7fiE SYS Y-regsave
D3DF 7fl3 SYS status reg save
D314-0315 7flfl-7flR IRQ vector (E&BF)
031b-0317 7RD-7qi Break interrupt vector (FEDE)
031fl-D31R 7C1E-7C13 NMI interrupt vector (FEAD)
D31&-D31B 7R4-7q5 OPEN vector (F4 DR)
D31C-D31D 7qt-7c17 CLOSE vector (F3 Ah)
D31E-031F 7Rfl-7c1(:l Set-input vector (FEC7)
03E0-D3E1 flDD-fiDl Set-output vector (F3DC1)
D3EE-D3E3 flDE-flD3 Restore I/O vector (F3F3)
03E4-Q3E5 fiO4-fiD5 INPUT vector (FEDE)
D3Efc-D3E7 flDb-flD7 Output vector (FE 7 a)
D3Efl-Q3ER flDfl-flDS Test-STOP vector (F7 7 D)
D3Eft-D3EB fllD-flll GET vector (F1F5)
D3EC-D3ED fllE-fll3 Abort I/O vector (F3EF)
03EE-D3EF ai^-ais USR vector (FEDE)
D33D-D331 fllfc-fll7 LOAD link
D33E-D333 fllfl-fllR SAVE link
D33C-D3FB flEfl-lDIR Cassette buffer
D40D-QFFF 10E4-4Dq5 3K RAM expansion area
1DDD-1FFF ^DRt-fllRl Normal BASIC memory
EDDD-7FFF fllRE-3E7t7 Memory expansion area
fidOO-fiFFF 3E7fcfl-3tfit3 Character bit maps (ROM)
APPENDIX C — VIC 20 187

Hex Decimal Description

Video interface chip (6560)


VIA (6522) interface-NMI
3715e-371b7 VIA (6522) interface-IRQ
S^DO-RSFF 37flflfl-3fl3clH Alternate color nybble area
Main color nybble area
ADDD-BFFF Plug-in ROM area
CDDO-FFFF ROM: BASIC and operating
system
788 MACHINE LANGUAGE FOR COMMODORE MACHINES

VIC 6560 Chip


Inter
$9000 lace Left Margin ( = 5) 36864

$9001 Top Margin (= 25) 36865


Scrn Ad
$9002 bit 9 # Columns ( = 22) 36866
bitO Double
$9003 # Rows ( = 23) Char 36867

$9004 Input Raster Value: bits 8-1 36868


Screen Address Character Address
$9005 36869
bits 13-10 bits 13-10

$9006 _ Light Pen Input Horizontal 36870


$9007 Vertical 36871

$9008 Paddle Inputs x 36872


$9009 36873
$900A ON i Voice 1 36874
$900B
-0N I Voice 2 Freque.icy —
36875
$900C ON }_ Voice 3 36876
$900D ON | Noise 36877

$900E Multi-Colour Mode ( = 0) Sound Amplitude 36878


Foregnd
$900F Screen Background Color
/Backg
Frame Color 36879

Figure C.5
APPENDIX C — VIC 6522 789

VIC 6522 Usage


DSR CTS DCD* Rl* DTR RTS Data
in in in in out out in

I3S-232 Interfa ce
$9110 37136
or, Parallej| User Port

$9111 Unused — see $911F 37137

$9112 DDRB(for$9110) 37138


$9113 DDRA(for$911F) 37139

$9114 JT1-L 37140


RS-232 Send Speed; —

$9115 T1-H 37141


Tape Write Timing
$9116 _T1 L atch L 37142

$9117 T1 L atchH 37143

$9118 T2-L 37144


RS-232 Input tinning —

$9119 T2-H 37145

$911A Shift Register funused) 37146

$911B T1 Control T2Cnt Shift Reg Control PBLE PALE 37147

$911C CB2: RS-232 Send CB1 C CA2: Tape motor Ctrl CA1 Ctl 37148

$911D CB1: CA1: 37149


NMI: T1 T2 RS-232 Restore
$911E in button 37150
ATN Tape —- Joyst cks — Serial Serial
$911F out sense Button Left Down Up Data in Clk in 37151

Figure C.6
190 MACHINE LANGUAGE FOR COMMODORE MACHINES

VIC 6522 Usage


Joystk I I Tape I
$9120 RightJ LoutJ 37152
Keyboard Row Select
$9121 Keyboard Column Input 37153
$9122 DDRB (for $9120) 37154
$9123 DDRA (for $9121) 37155
$9124 T1-L 37156
Cassette Tape Read; —

$9125 T1-H 37157


Keyboard & Clock
$9126 _T1-L L.atch 37158
Interrupt Timing —

$9127 T1-H Latch 37159


$9128 _T2-L Serial Bus Timing 37160
$9129 T2-H Tape R/W Timing 37161
$912A Shift Register (*Unused) 37162
$912B T1 Control T2 Ctrl Shift Register Contrl PBLE PALE 37163
CB1 CA1
$912C Serial Bus Data Out Contl Serial Clock Line out Contl 37164

$912D CB1:* CA1: 37165


$912E IRQ: T1 T2 SRQin Tape in 37166
$912F *Unused: see $9121 37167

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.

Most zero-page locations may be copied to another part of memory so


that their original contents can be restored after use. The programmer
should take great care, however, in modifying the following locations, which
are critical within the operating system or BASIC: $13, $lbto$lfl,
$2Bto$3fl, $3fl, $53 to $54, $bfl, $73 to $flR, $qD to $=16, $RD
to $&E, $Bfi to $BA, $C5 to $F4.

Memory Map
Hex Decimal Description

DDDD D Chip directional register


DDD1 1 Chip I/O; memory and tape
control
DDD3-0DD4 3-4 Float-fixed vector
DDD5-D00b 5-b Fixed-float vector
DDD7 7 Search character
DDOfl fi Scan-quotes flag
Dooq q TAB column save
DDDfl ID D = LORD, 1 = VERIFY
DDDB 11 Input buffer pointer/number of
subscripts
DDDC IE Default DIM flag
DDDD 13 Type:$FF= string;
DD= numeric
DDDE IA Type: $ fl D = integer;
D D = floating point
DDDF 15 DATfi scan/LIST quote/memory
flag
DD1D 1b Subscript/FNx flag
DD11 17 D = INPUT;$4D = GET;
$qfl=RE&D
DD1E Ifl RTN sign/Comparison
evaluation flag
DD13 iq Current I/O prompt flag
DD14-D015 ED-El Integer value
792 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DDlb Pointer: temporary string stack


DD17-DDia E3-E4 Last temporary string vector
ooiq-0021 E5-33 Stack for temporary strings
DDEE-DDE5 34-37 Utility pointer area
DDEt-DDEfi 3fl-4E Product area for multiplication
DDEB-DDEC A3-AA Pointer: start-of-BASIC
DDED-DDEE AS-Ak Pointer: start-of-variables
DDEF-DD3D Al-Ab Pointer: start-of-arrays
DD31-DD3E 4q-5D Pointer: end-of-arrays
DD33-DD34 51-5E Pointer: string-storage (moving
down)
0035-003^ 53-54 Utility string pointer
DD37-DD3fl 55-5t Pointer: limit-of-memory
DD3q-DD3ft 57-5fl Current BASIC line number
DD3B-DD3C 5H-tD Previous BASIC line number
DD3D-DD3E tl-t5 Pointer: BASIC statement for
CONT
0D3F-DD4D t3-fc4 Current DflTA line number
D041-D04E t5-tt Current DATA address
DD43-DD44 Input vector
DD45-QD4b tq-?D Current variable name
0D47-0D4fi 71-75 Current variable address
Q04R-DD4A 73-74 Variable pointer for FOR/NEXT
QD4B-DQ4C 75-7t Y-save; op-save; BASIC
pointer save
DD4D 77 Comparison symbol
accumulator
0D4E-Q053 7fl-fl3 Miscellaneous work area,
pointers, and so on
QD54-Q05b fi4-flb Jump vector for functions
DD57-DDbD fl7-qt Miscellaneous numeric work
area
DDbl R7 Accum#1: exponent
DDbE-D0b5 qa-iDi Accum#1: mantissa
DDbfc IDE Accum#1: sign
DDfc7 1D3 Series evaluation constant
pointer
OObfi 1D4 Accum#1 hi-order (overflow)
DDfcR-DDtE 1D5-11D Accum#2: exponent, and so on
DDfcF 111 Sign comparison, Acc#1 versus
#2
DD7D HE Accum#1 lo-order (rounding)
APPENDIX C — COMMODORE 64 793

Hex Decimal Description

0071-007E 113-114 Cassette buffer length/series


pointer
D073-D0flA 115-136 CHRGET subroutine; get
BASIC character
DD7A-DD7B 155-153 BASIC pointer (within
subroutine)
DDflB-DDflF 131-143 RND seed value
0010 144 Status word ST
Keyswitch PI A: STOP and
RVS flags
0015 14b Timing constant for tape
0013 147 Load = D; verify = l
0014 14fi Serial output: deferred character
flag
0015 141 Serial deferred character
001b 150 Tape EOT received
0017 151 Register save
OOlfl 1SE How many open files
0011 153 Input device, normally D
001A 154 Output CMD device, normally 3
001B 155 Tape character parity
001C 15b Byte-received flag
0D1D 157 Direct =$flD/RUN = D output
control
001E ISO Tape pass 1 error log/character
buffer
001F 151 Tape pass E error log corrected
00A0-00A2 ibQ-lfcS Jiffy Clock HML
00A3 Ib3 Serial bitcount/EOI flag
00A4 Ib4 Cycle count
0DA5 Ib5 Countdown, tape write/bit count
OOAb Ibb Tape buffer pointer
00A7 Ib7 Tape write leader count/read
pass/inbit
OOAfi Ibfl Tape write new byte/read
error/inbit count
D0A1 Ibi Write start bit/read bit error/stbit
OOAA 170 Tape Scan;Cnt;Load;
End/byte assembly
OOAB 171 Write lead length/read
checksum/parity
OOAC-ODAD 175-173 Pointer: tape buffer, scrolling
794 MACHINE LANGUAGE FOR COMMODORE MACHINES

iex Decimal Description

DQAE-QDAF 174-175 Tape end address/end of


program

DDBD-DDB1 17t-177 Tape timing constants


DQBE-0DB3 17fl-17q Pointer: start of tape buffer
QQBA iflD 1 = tape timer enabled; bit
count
ODBS Ifll Tape EOT/RSE3E next bit to
send
DDBfc ifiE Read character error/outbyte
buffer
DDB7 163 Number of characters in file
name
DDBfl l&A Current logical file
DDBq Ifl5 Current secondary address
DDBA i&y Current device
DDBB-DDBC Ifl7-lfifl Pointer to file name
ODBD iaq Write shift word/read input char
DDBE ird Number of blocks remaining to
write/read
DDBF iqi Serial word buffer
DDCD iqa Tape motor interlock
DOC1-ODCE 1R3-1H4 I/O start address
DQC3-00C4 iqs-iqt Kernel setup pointer
DDC5 1R7 Last key pressed
DDCt iqa Number of characters in
keyboard buffer
DDC7 iqq Screen reverse flag
DDCfl EDD End-of-line for input pointer
DDCq-DDCA EDl-EDE Input cursor log (row, column)
DDCB 5D3 Which key: hA if no key
DDCC 2UA D= flash cursor
DDCD EDS Cursor timing countdown
DDCE EDt Character under cursor
DDCF ED7 Cursor in blink phase
DDDD EDfi Input from screen/from
keyboard
0DD1-00D2 EDR-E1D Pointer to screen line
DDD3 Ell Position of cursor on above line
00D4 E1E 0 = direct cursor; else
programmed
DDD5 E13 Current screen line length
APPENDIX C — COMMODORE 64 795

Hex Decimal Description

DDDt E14 Row where cursor lives


DDD? E15 Last inkey/checksum/buffer
DDDfl Elt Number of INSERTS
outstanding
DDDq-DDF5 E17-E^E Screen line link table
00F3-DDF4 Screen color pointer
DDF5-DDFb Keyboard pointer
DDF7-DDFfl E47-E4fl RS-232 Rev pntr
DDFq-DDFft E^R-EED RS-232 Tx pntr
DQFF-01DA E5t-Etb Floating to ASCII work area
D1DD-1D3E E5b-31fl Tape error log
D1DD-D1FF E5fc-511 Processor stack area
D£DQ-D£5fl 51S-fcDD BASIC input buffer
0E5R-DELE tDl-tlD Logical file table
DEb3-QEkC fcll-tED Device number table
DEbD-DE7b tEl-t3D Secondary address table
0E77-D£fiD t31-t^D Keyboard buffer
DEfil-DEfiE b41-fc4E Start of BASIC memory
DEfl3-DEfl4 ^A3-\dAA Top of BASIC memory
DEflS Serial bus timeout flag
DEflfc Current color code
DEfl7 b^7 Color under cursor
DEflfl b4fl Screen memory page
DSflq Maximum size of keyboard
buffer
DEflR t5D Repeat all keys
DEflB t51 Repeat speed counter
DEflC L5E Repeat delay counter
DEflD L53 Keyboard Shift/Control flag
DEflE Last shift pattern
DEflF-DSqD b55-b5b Keyboard table setup pointer
DERI b57 Keyboard shift mode
DERE b5fl D = scroll enable
DER3 RS-232 control reg
bbD RS-232 command reg
DE^E-DERb fcfcl-fctE Bit timing
DER7 fcb3 RS-232 status
DERfl bb4 Number of bits to send
DERR-DSRa Lb5 RS-232 speed/code
DERB fcfc7 RS232 receive pointer
DEqc bbfl RS232 input pointer
796 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DEqD bbq RS232 transmit pointer


DEqE b7D RS232 output pointer
DEqF-DERD b71-b7E IRQ save during tape I/O
DERI b73 Clft E (NMI) interrupt control
DERE h?4 CIA 1 timer A control log
DSR3 b75 CIR 1 interrupt Log
0ER4 b7b CIA 1 timer A enabled flag
DER5 b77 Screen row marker
DECD-DEFE 7D4-7bb (Sprite 7)
D3DD-D3D1 7bfl-7bq Error message link
D3DE-D3D3 77D-771 BASIC warm start link
0304-Q3DS 77E-773 Crunch BASIC tokens link
D3Db-D3D7 774-775 Print tokens link
D3Dfl-D3Dq 77b-777 Start new BASIC code link
03DR-D3DB 77fl-77q Get arithmetic element link
D3DC 7flD SYS A-reg save
D3DD 7fll SYS X-reg save
03DE 7fiE SYS Y-reg save
D3DF 7B3 SYS status reg save
D31D-D31E 7fl4-7fl5 USR function jump (BE4fi)
0314-0315 7flfl-7flq IRQ vector (EA31)
031b-D317 ?qD-7qi Break interrupt vector (FEbb)
D31fl-03iq
™j n p "j n^]
NMI interrupt vector (FE47)
031R-031B
■p n / "j n r
OPEN vector (F3 A A)
D31C-D31D
"j n r ~? r*i ~i
CLOSE vector (FERl)
D31E-D31F
T Q D TQQ
Set-input vector (FEDE)
D3ED-D3E1 fiDQ-flDl Set-output vector (FESD)
D3EE-D3E3 flDE-flD3 Restore I/O vector (F333)
D3E4-D3E5 fiD4-flD5 Input vector (F157)
D3Eb-D3E7 flDb-flD7 Output vector (F1CA)
D3Efl-03Eq flDfl-flOq Test-STOP vector (FfcED)
D3ER-D3EB fllD-flll GET vector (F13E)
D3EC-D3ED fllE-fll3 Abort I/O vector (F3EF)
D3EE-D3EF fll4-fll5 OSR vector (FEfcfc)
D33D-D331 aib-ai7 LOAD link(F4A5)
D33E-D333 flia-aiq SAVE link (FEED)
D33C-D3FB aEfl-iDiq Cassette buffer
D34D-037E fl3S-flq4 (Sprite 13)
D3fi0-03BE aqb-qsa (Sprite 14)
D3CD-D3FE qbD-lDEE (Sprite 15)
D4DD-D7FF 1DE4-ED47 Screen memory
BASIC RAM memory
in / d / n n r* n
DflDD-qFFF C LJ ^ O ~~ n u n j n
APPENDIX C — COMMODORE 64 797

Hex Decimal Description

Alternative: BOM plug-in area


ADDO-BFFF ROM: BASIC
A000-BFFF Alternate: BAM
CDDO-CFFF BAM memory, including
alternative
D00D-DD2E 53246-532=14 Video chip (t 5 bb)
D4D0-D41C 54272-543DD Sound chip (t561 SID)
DflDD-DBFF 552qt-5E>3iq Color nybble memory
DCDD-DCDF 5b32D-5b335 Interface chip 1, IRQ (t52b
CIA)
DDDD-DDDF 5t57t-5b5Sl Interface chip 2, NMI (b55b
CIA)
DDDD-DFFF Alternative: character set
EDDD-FFFF S13A4-LSS3S ROM: operating system
EDDD-FFFF Alternative: RAM
798 MACHINE LANGUAGE FOR COMMODORE MACHINES

CIA 1 (IRQ) (6526) Commodore 64

Paddle SEL Joystick 0

A . B R . L . D . U
$DC00 PRA 56320
Keyboard Row Select (inverted)
Joystick 1
$DC01 PRB 56321
Keyboard Column Read

$DC02 $FF — All Output DDRA 56322

$DC03 $00 — All Input DDRB 56323

$DC04 TAL 56324


— Timer A —
$DC05 TAH 56325

$DC06 TBL 56326


— Timer B —
$DC07 TBH 56327

Tape Timer Interr.


$DC0D IER 56333
input i
B
, A
Time Timer
One Out PDL A
$DC0E 1 |
shot pode out , start CRA 56334
Time Timer
One Out PBC B
$DC0F shot /node out , start CRB 56335
1 1 1

Figure C.8
APPENDIX C — COMMODORE 64 799

CIA 2 (NMI) (6526) Commodore 64

Serial Clock Serial Clock ATN RS-232


$DD00 Video Block PRA 56576
In In Out Out Out Out

DSR CTS DCD* Rl* DTR RTS RS-232

$DD01 In In In In Out Out In PRB 56577


F>arallel IJser Po rt

In In Out Out Out Out Out Out


$DD02 DDRA 56578
$3F

$DD03 $06 Fo r RS-232 DDRB 56579

$DD04 TAL 56580


— Timer A —

$DD05 TAH 56581

$DD06 TBL 56582


— Timer B —

$DD07 TBH 56583

RS-232 Timer Timer


$DD0D ICR 56589
In B , A
Timer
$DD0E CRA 56590
, A Start

Timer
$DD0F CRB 56591
, B Start

Figure C.9
200 MACHINE LANGUAGE FOR COMMODORE MACHINES

C64 Memory Map


6566 Video — Sprite Registers
Sprite Sprite Sprite Sprite
0 7 0 7

D000 DOOE Position X 53248 53262

DOO1 DOOF Y 53249 53263

D027 D02E Color 53287 53294

Sprite Bit Positions

7 6 5 4 3 2 10

DO1O X-position high 53264

D015 Sprite Enable 53269

D017 Y-expand 53271

DO1B Background Priority 53275

D01C Multicolor 53276

D01D X-expand 53277

DO1E Interrupt: Sprite collisn 53278

DO1F Interrupt: Sprite/Backgrd coll 53279

C64 Memory Map


6566 Video - Control and Misc. Registers
D011 Extnd Color Bit Map Dsply Enabl Row Selct Y-Scroll 53265

D012 Raster Register 53266

D013 53267
Light Pen Input
DOM 53268

D016 | Multicolor ColmSel X-Scroll 53270

D018
vm13
Screen (Video Matrix)
vm12 vm11 vm10 cb13
Character Base
. cb12 . cb11 I x 53272

D019 IRQ IRQ sence LP SSC SBC 53273

Light Collision
D01A IRQ Enable Pen, Sprt , Back , Rastr 53274

COLOUR REGISTERS

D020 Exterior 53280

D021 Background #0 53281

D022 Background #1 53282

D023 Background #2 53283

D024 Background #3 53284

D025 Sprite Multicolor #0 53285

D026 Sprite Multicolor #1 53286

Figure C.10
APPENDIX C — COMMODORE PLUS/4 201

SID (6581) Commodore 64

V1 V2 V3 V1 V2 V3
D400 D407 D40E L_ 54272 54279 54286
— Frequency
D401 D408 D40F H 54273 54280 54287

D402 D409 D410 Pulse Width L_ 54274 54281 54288

D403 D40A D411 0 0 H 54275 54282 54289

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

D416 Filter Frequency H 54294

Resonance Filter voices


D417 54295

Passband Master
D418 OFF, H, , BO, 10 I y^™, 54296

Filter & Volume


(write only)

D419 Paddle X 54297

D41A Paddle Y 54298

D41B Noise 3 (random) 54299

D41C Envelope 3 54300

Sense
(read only)

Special voice features (TEST, RING MOD, SYNC) are


omitted from the above diagram.

Figure C.11
202 MACHINE LANGUAGE FOR COMMODORE MACHINES

Commodore PLUS/4 "TED" Chip-


Preliminary
At time of publication the Commodore 264 (alternatively called Plus/4) and
a related machine, the Commodore 16, are not commercially available.
Design details could change before commercial release.

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.

Memory Map, Preliminary


Much of zero-page is the same as for the Commodore 64. Some differ
ences, and other information:

Hex Decimal Description

D073-DDflfl 115-135 (CHRRGET not present)


DDR? 151 How many open files
DORS 155 Input device, normally D
DDHR 153 Output CMD device, normally 3
DDfiC 17E Current logical file
DORD 173 Current secondary address
DORE 174 Current device
00RF-D0BD 175-17fc Pointer to file name
DDCfl-DDCR EDD-E01 Pointer to screen line
DDCR EDE Position of cursor on above line
DDCD ED5 Row where cursor lives
DDEF E3R Number of characters in
keyboard buffer
D314-D315 7fifl-7flq IRQ vector (CEDE)
031b-D317 7RD-7qi Break interrupt vector (FA A B)
D31fl-D31R 7RE-7R3 OPEN vector (EF53)

(Most other vectors are similar to the C64, but are two locations lower)

D5DD-D50E lEfiD-lEflE USR program jump


D50R-D51E lEflR-lESfl Logical file table
D513-D51C lEqq-13Dfl Device number table
D51D-D5Eb 13Dq-131fl Secondary address table
05E7-0530 13iq-133fl Keyboard buffer
DflDD-DBE? ED4fl-3D47 Color memory
DCDD-DFE7 3D7E-4D71 Screen memory
APPENDIX C — B SERIES 203

IN: IN: IN: IN: OUT: OUT: OUT: OUT:


TAPE TAPE
SERIAL CLOCK TAPE ATN SERIAL
MOTOR CLOCK

FFOO 65280
T1
FFO1 65281

FFO2 65282
TIMERS T2
FFO3 65283

FF04 65284
T3
FFO5 65285

FFO6 TEST ECM BMM BLANK ROWS Y-ADJUST 65286

FFO7 RVS OFF t PAL , FREEZE , MCM |COLUMNSa X-ADJUST 65287

FF08 KEYBOARD LATCH 65288

FFO9 T1 LP , RAST 65289

FFOA IER T1 LP , RAST 65290

FFOB RASTER CONTROL 65291

FFOC 65292

FFOD CURSOR CONTROL 65293

FFOE VOICE 1 65294


SOUND:
FFOF VOICE 2 65295

FF1O 2 HI 65296

FF11 SOUND SELECT VOLUME 65297

FF12 BIT MAP BASE R BANK VOICE 1 HI 65298

FF13 CHARACTER BASE SCLOCK STATUS 65299

FF14 VIDEO MATRIX 65300

FF15 65301

FF16 65302

FF17 65303

FF18 65304

FF19 65305

FF1A 65306

FF1B CHARACTER POSITION RELOAD 65307

FF1C 65308

FF1D VERTICAL LINE REGISTER 65309

FF1E HORIZONTAL POSITION REGISTER 65310

FF1F BLINK COUNT V SUBADDRESS 65311

FF3E ROM SELECT 65342


WRITE ONLY:
FF3F RAM SELECT
65343

Figure C.12
204 MACHINE LANGUAGE FOR COMMODORE MACHINES

1DDD-FFFF 4Dqb-bS535 BASIC RAM memory


flDDO-FFFF 3E7bfl-b553S ROM: BASIC
FFDD-FF3F b5EfiQ-b5343 TED I/O control chip

B Series (B-128, CBM-256, etc.)


The Great Zero-Page Hunt
Zero page has a different meaning on the B series. There are several
zero pages. Usually, you'll want to use values from bank 15 (the ROM
bank, where system variables are kept); but if you are writing programs
that will reside in a different bank, you'll have all of zero page (except
locations D and 1) completely at your disposal.

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.

Most zero-page locations may be copied to another part of memory so


that their original contents can be restored after use. The programmer
should take great care, however, in modifying the following locations, which
are critical within the operating system or BASIC: $1&, $1D to $E1,
$ED to $41, $43, $5B, $7fl, $fi5-fl7, $qE to $&B, $CD to $E5.

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.)

Notable features as compared to previous Commodore products include:


—CHRGOT is no longer in RAM. Wedge-type coding must be inserted at links
SDEREandSDEAD, which is likely to make the job easier.
—BASIC vectors have "split." Now, for example, there are discrete "start of
variables" and "end of variables," distinct from end-of-BASIC and start-of-
arrays. Three-byte vectors (including bank number) are not uncommon.
—The "jump table" at top of memory is still accessible and reasonably consistent
with previous Commodore products.
—Simple machine language programs will fit into the spare 1 K of RAM at
$D4DDto$D7FF without trouble. Large programs must be implemented
APPENDIX C — B SERIES 205

either by plug-in memory (RAM or BOM) in bank 15 or by being placed into


another bank (preferably bank 3). Supplementary code will be needed to
make all the coding components fit.

The following map contains BASIC addresses specific to the B256/80;


references to banks D to A are also specific to that machine. Most of the
map is of general usage, however.

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

ddie Type: 12fi= integer,


D= floating point
DD13 1H Crunch flag
DD14 ED Subscript index
DD15 El lnput=D; get=fc4; read =
DDlt-DQiq EE-E5 Disk status work values
ODlft Eb Current I/O device for prompt
suppress

DD1B-DD1C E7-E3 Integer value


DD1D-DD1F Eq-31 Descriptor stack pointers
DDED-DDE1 3E-33 Vector to string descriptors
DDES-DDEB 34-43 Miscellaneous work pointers
DOED-DDEE 45-4b Start-of-BASIC pointer
DDEF-DD3D 47-48 End-of-BASIC pointer
DD31-DD3E 4q-5D Start-of-Variables pointer
DD33-DD34 51-5E End-of-Variables pointer
QD35-DQ3E. 53-54 Start-of-Arrays pointer
DD37-DD3a 55-5t End-of-Arrays pointer
DD3q-DD3A 57-5B Variable work pointer
DD3B-DD3C 5q-bD Bottom-of-Strings pointer
DD3D-DD3E fcl-tE Utility string pointer
DD3F-DD41 t»3-b5 Top of string memory pointer
DD4E-DD43 fct-fc7 Current BASIC line number
DD44-0D45 ta-tq Old BASIC line number
DD4b-DD47 7D-71 Old BASIC text pointer
DD4q-DD4A 73-74 Data line number
DD4B-DD4C 75-7fc Data text pointer
DD4D-DD4E 77-7B Input pointer
DD4F-DD5D "5 O API
f " ~~ Q LJ Variable name
DD51-DD53 ai-a3 Variable address
DD54-DD5t a4-at For-loop pointer
0D57-DD5a a?-aa Text pointer save
DD5A qo Comparison symbol
accumulator
DD5B-DD5D qi-qs Function location
DD5E-DDE.0 q4-qb Working string vector
DDtl-DDb3 q?-qq Function jump code
DDfc4-D0tE 10D-11D Work pointers, values
DDfcF in Exponent sign
QD7D HE Acum string prefix
DD71 113 Acum#1: exponent
DD7E-DD75 114-117 Accum#1: mantissa
APPENDIX C — B SERIES 207

007b llfl Accum#1: sign


0077 nq Series evaluation constant
pointer
007fi 1E0 Acum#1 hi order (overflow)
007q-007E lEl-lSfc Accum#2
007F 1E7 Sign comparison, Acc#1 versus
#2
OOflO lEfl Acc#1 low-order (rounding)
OOfll-OOfl^ lEq-13E Series, work pointers
00fl5-00fl7 133-135 BASIC text pointer
ODflfl-ooaq 13t-137 Input pointer
OOflB-OOflE Jj J M -^ Jj ^i c. DOS parser work values
OOfiF Error type number
ooqo-ooqE 144-l^fc Pointer to file name
Pointer: tape buffer, scrolling
nSqb-00^5 150-15E Load end address/end of
ooqaa program
nnnn
LJ LJ i
nnnd
i ~* LJ LJ i D 153-155 I/O start address
ooqc 15b Status word ST
ooqD 157 File name length
ooqE 15fl Current logical file
ooqF isq Current device
OQRO ibD Current secondary address
00A1 Ibl Input device, normally 0
OOAE ibE Output CMD device, normally 3
OOAt-OORfi Itt-ltfl INBUF
ooaq itq Keyswitch PI A: stop key, etc.
ODRfi 170 IEEE deferred flag
OORB 171 IEEE deferred character
OORC-DOftD 17E-173 Segment transfer routine vector
00AE-00B3 i7^-i7q Monitor register save
OOB^i lao Monitor stack pointer save
00B5 lai Monitor bank number save
00B7-D0Bfl Ifl3-lfl4 Monitor IRQ save/pointer
DDBq-OOBA Ifl5-lfit Monitor memory pointer
OOBB-OOBC Ifl7-lfifi Monitor secondary pointer
DDBD iaq Monitor counter
OOBE iqo Monitor miscellaneous byte
DDBF iqi Monitor device number
00CD-00C1 iqE-iq3 Programmable key table
address
00CE-00C3 Programmable key address
208 MACHINE LANGUAGE FOR COMMODORE MACHINES

0DC4-DDC7 1R t—1R *-! Pointers to change


programmable key table
DDCfl-DDCR 5DD-ED1 Pointer to screen line
DDCR SDE Screen line number
DDCB ED3 Position of cursor on line
DOCC 2QA D = text mode, else graphics
mode
DDCD ED5 Keypress variable
DDCE EOt Old cursor column
DDCF SD7 Old cursor row
DDDD SDfl New character flag
DDD1 Eoq Number of keys in keyboard
buffer
DDDE E1D Quotes flag
DDD3 Ell Inert key counter
0DD4 E1E Cursor type flag
DDD5 E13 Screen line length
DDDt S14 Number of keys in "key" buffer
DDD7 E15 Key repeat delay
DDDfl Elt Key repeat speed
DDDq-DDDfi E17-E1S Temporary variables
DDDB E1H Current output character
DDDC EED Top line of current screen
DDDD EE1 Bottom line of screen
DDDE Left edge of current screen
DDDF EE3 Right edge of screen
DDED 22A Keys: E55 = none; lE7=key,
lll = shift
ODE1 EE5 Key pressed: E 5 5 = no key
DDEE-DDE5 EEt-EER Line Wrap Bits
D1DD S5b Hex to binary staging area
D1DD-D1DR S5t-Ebt Numeric to ASCII work area
D1DD-D1FE E5t-51D Stack area
D1FF 511 Stack pointer save location
DEDD-DEDF 51E-5E7 File name area
DElD-DEEb 5Efl-55D Disk command work area
DE55-DE5b 5R7 Miscellaneous work values for
WRIT, etc
DE57 5qq "Bank" value
DESfl fcDD Output logical file (CMD)
DESH tDl Sign of TRN
APPENDIX C — B SERIES 209

QE5A-DESD fcDE-LD5 Pickup subroutine;


miscellaneous work values
DE5E-0E7b bDt-t3D PRINT USING working
variables
QEfiO-OEfll Error routine link [fl555]
0EflE-DEfl3 fc^E-t.43 Warm start link [fl 5CD]
0Efl4-DEfl5 fcz;zi_b/i5 Crunch token link [flflCE]
DEfifc-DSfl? List link [flRF4]
DEfifl-OEflR b^fl-fc^iR Command dispatch link [fl7 54]
DEfiA-DEflB b5D-fc51 Token evaluate link [RfcBl]
DEfiC-DEflD fc5E-b53 Expression eval link [R5C4]
DEflE-DEflF b5^-fc55 CHRGOT link[BASC]
QERD-DER1 fc5b-b5? CHRGET vector [BA3E]
DERE-QER3 b5fl-fc5R Float-fixed vector [BA1E]
0ER4-DER5 bbD-bbl Fixed-Float vector [RD3R]
DERb-DER7 fcfcE-fcb3 Error trap vector
OERfl-DERR bb^-bb5 Error line number
OERa-DERB bfcb-fcfc? Error exit pointer
DERC bbfl Stack pointer save
DERD-DERF bbR-fc71 Temporary TRAP, DISPOSE
bytes
DEAD-QEA5 b?E-fc?? Temporary IN S T R $ bytes
DEAb-DEA7 b?fl-b7R Bank offset
0300-D301 7fcfl-7fcR IRQ vector [FBER]
03DE-D303 77D-771 BRK vector [EEEl]
D304-Q3D5 77E-773 NMI vector [FCAA]
D3Qb-D307 77^-775 OPEN vector [FLBF]
D3Dfl-D3DR 77fc-777 CLOSE vector [F5ED]
D3DA-D3DB 77B-77R Connect-input vector [F54R]
D3DC-030D 7flD-7fll Connect-output vector [F5A3]
D3DE-D3DF 7flE-7fl3 Restore default I/O vector
[FfcAb]
D31D-Q311 7fl^-7fl5 Input vector [F4RC]
D31E-D313 7flb-7fl7 Output vector [F4EE]
0314-0315 7flfi-7flR Stop key test vector [FRfcB]
031b-0317 7RD-7R1 GET vector [F43D]
031fl-031R 7RE-7R3 Abort all files vector [Fb7F]
031A-D31B 7R4-7R5 Load vector [F7 A fc]
D31C-031D 7RL-7R7 Save vector [Ffl<C]
D31E-D31F 7RB-7RR Monitor command vector
[EE77]
210 MACHINE LANGUAGE FOR COMMODORE MACHINES

03ED-Q3E1 flDD-fiDl Keyboard control vector [EDIF]


D3EE-Q3E3 BDE-SD3 Print control vector [EDIF]
03E4-D3E5 flD<-flD5 IEEE send LSA vector
[F574]
D3Eb-D3E7 BDt-fiD7 IEEE send TS& vector
[FEflD]
D3Efl-Q3Zq flDfl-flDq IEEE receive byte vector
[F3DA]
Q3EA-03EB fllD-fill IEEE send character vector

03SC-03SD 015-013 IEEE send untalk vector


[F2flB]
03EE-D3EF 0K-01S IEEE send unlisten vector
[F5AF]
D33D-D331 fllt-017 IEEE send listen vector
[F234]
Q33E-D333 fllfl-fliq IEEE send talk vector [F53D]
0334-Q33D flED-aeq File logical addresses table
033E-Q347 030-33^ File device table
0343-0351 File secondary address table
035E-0354 fiSD-flSE Bottom of system memory
0355-0357 853-855 Top of system memory
035fl-D35A ast-asa Bottom of user memory
035B-035D osq-oti Top of user memory
035E atE IEEE timeout; D= enabled
D35F at3 D = load; IEfl= verify
03b0 at< Number of open files
03bl ats Message mode byte
03b3-03bb at7-a7D Miscellaneous register save
bytes
03tq 373 Timer toggle
03bfi-03bB 874-375 Cassette vector (dead end)
D3tF-D371 a?q-aai Relocation start address
D375 aas Cassette motor flag (unused)
D37t-D377 aat-aa? RS-232 control, command
D37R aqD RS-232 status
D37B aqi RS-232 handshake input
D37C aqE RS-232 input pointer
D37D aq3 RS-232 arrival pointer
D3flD-D3flE aqt-aqa Top of memory pointer
aqq-qia Programmed key lengths
APPENDIX C — B SERIES 211

D3R7 qiq RVS flag


D3qa RED Current line number
D3qq RE1 Temporary output character
save

D3RA q22 D = normal, E 5 5 = auto insert


D3RB RE3 D = scrolling, 1S fl = no scroll
D3RC RE4 Miscellaneous work byte for
screen

D3RD RE5 Index to programmed key


D3CIE REt Scroll mode work flag
D3RF qs? Bell mode flag
D3AD REfl Indirect bank save
D3A1-D3AA qgq_q3Q
Bit mapped tab stops
D3AB-D3B4 q3q-qz;fl Keyboard input buffer
D3B5-03Bb 'Key'word link [EqIB]
D3Ffl-D3Fcl IDlt-lDl? Restart vector
D3FA-D3FB IDlfl-lDIR Restart test mask
D4DD-D7FF 1QE4-ED47 Free RAM (reserved for DOS )
DflDD-DFFF ED^ifl-^DRS Reserved for plug in RAM
1DDD-1FFF zjQqfc-fliqi Reserved for plug in DOS ROM
5DDD-7FFF fllcIE-E37t.7 Reserved for cartridges
flDDD-BFFF 3E7tfl-^!c1151 BASIC ROM
CDDD-CFFF 4c11i5E-53E47 Unused
DDDD-D7CF 53E4fl-55S47 Screen RAM
DfiDD-DQDl SSERb-SSER? Video controller fc.545
DADD-DA1C 55flDfi-55fl3t Sound interface device fc5fll
DBDD-DBDF 5tDt4-5tD7q Complex interface adaptor

DCDD-DCDF 5t3ED-5t335 Complex interface adaptor

DDDD-DDD3 5t57fc-5fc57q Asynchronous communications


IA b551
DEDD-DED7 5tfl3S-5tfl3q Tri Port Interface Adaptor

DFDD-DFD7 57Dflfl-57Dq5 Tri Port Interface Adaptor

EDOD-FFFF 573^4-b5535 Kernal ROM

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

6545 CRT Controller

D800 D801 Typical Value


55296 55297 (Decimal)

0 Horizontal Total 108 or 126 or 127

1 Horizontal Char Displayed 80

2 Horizontal Sync Position 83 or 98 or 96


v Sync Width H 15 or 10
3

4 Vertical Total 25 or 31 or 38

5 Vert Total Adjust 3 or 6 or 1

6 Vertical Displayed 25

7 Vert Sync Position 25 or 28 or 30

8 Mode 0

9 Scan Lines 13 or 7

10 Cursor Start 96 (blink) or


0 or 6 (underline)

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

6525 Tri Port

DEOO NRFD NDAC EOI DAV | ATN RFN 56832


Cassette Network
DE01 Sense Motor Out ARB Rx Tx SRQ IFC 56833

DE02 IRQ ACIA IP | CIA-2 IEEE PWR 56834

DE03 Data Direction Register For DEOO 56835

DE04 Data Direction Register For DE01 56836

DE05 IRQ ACIA IP | CIA-2 IEEE PWR 56837


CB CA IRQ
DE06 Graphics Stack On 56838

DE07 Active Interrupt Register 56839

6525 Tri Port 2

DFOO Keyboard 57088

DF01 Select 57089


CRT
DF02 Mode
Keyboard Read 57090

DF03 Data Direction Register for DFOO (out) 57091

DF04 Data Direction Register for DF01 (out) 57092

DF05 Data Direction Register for DF02 (in) 57093

DF06 Unused 57094

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.

Architecture: "Bank numbers" as used in BASIC BANK and the MLM


addressing scheme are misleading; in fact, they are more correctly "con
figuration numbers." Bank 0 shows RAM level 0, which contains work
areas and the user's BASIC program. Bank 1 also shows RAM, this time
(for addresses above hexadecimal D4DD) level 1 which contains vari
ables, arrays, and strings. Other "banks" are really configurations, with
various types of ROM or I/O overlaying RAM. Thus, Bank 15 (the most
popular) is ROM and I/O covering RAM bank 0. Bank 14, however, is
ROM and the character generator overlaying RAM Bank 0. Architecture
is set so that addresses below $ D A D D reference Bank 0 only. Other bank
switching (more complex than the simplified 16-bank concept) is accom
plished via storing a mask to address $FFDD, or calling up prestored
masks by writing to $FFD1-FFD4.

The Great Zero-Page Hunt:


Locations $FA to $FF are available. Locations $24 to $2C, $5D to
$55, and $5C) to $b2 are work areas available for temporary use.

Most zero-page locations may be copied to another part of memory so


that their original contents can be restored after use. The programmer
should take great care, however, in modifying the following locations, which
are critical within the operating system or BASIC: $15, $lfl to $1A,
$ED to $3E, $5t to $57, $qD to $RA, $AD to $62, $A7 to $Afl,
$BA to $Bt, $BD, $CD, $Cfl to $Dfl.
APPENDIX C — COMMODORE 128 215

Memory Map
ALL BANKS:

Hex Decimal Description

DDDD D I/O directional register


DDD1 1 I/O port, similar to C64
D00E-QD04 2-A SYS address, MLM registers
(SR, PC)
ODDS-ODD^ SYS, MLM register save (A, X,
Y, SR/SP)
DODfl ID Scan-quotes flag
ODDB 11 TAB column save
DDDC IE D = LOAD, 1 = VERIFY
DDDD 13 Input buffer pointer/number of
subscripts
DDDE IA Default DIM flag
OODF 15 Type: FF = string; DD=numeric
DD1O It Type: fi D = integer;
DD=floating point
DD11 17 DATA scan/LIST quote/memory
flag
001E Ifl Subscript/FNx flag
DD13 iq D = INPUT;$4D = GET;
$qfl=RERD
QU1A ED ATN sign/Comparison
evaluation flag
DD15 El Current I/O prompt flag
0Dlt-DD17 EE-E3 Integer value
DDlfl EA Pointer: temporary string stack
D01R-DDE3 E5-35 Stack for temporary strings
00E4-D0E7 Utility pointer area
DOEfl-DOEC AD-AA Product area for multiplication
DDED-DOEE AS-Al, Pointer: start-of-BASIC (for
Bank 0)
DDEF-D030 A1-A& Pointer: start-of-variables
(Banki)
DD31-0D3E <q-5D Pointer: start-of-arrays
0033-D034 51-5E Pointer: end-of-arrays
0D35-DD3E. 53-54 Pointer: string-storage (moving
down)
216 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

D037-QD3fl 55-Sb Utility string pointer


0Q3q-D03fi 57-5fl Pointer: limit-of-memory
(Banki)
DD3B-DD3C sq-tD Current BASIC line number
DD3D-DD3E ti-ta Textpointer: BASIC work point
0D3F-DD40 ys-hA Utility Pointer
QQA1-OUAE Current DATA line number
QUA3-0UAA t7-tfl Current DATA address
UQAS-UDAy tq-7D Input vector
QQA7-UQA& 71-7E Current variable name
QUAI-QUAA 73-74 Current variable address
UUAB-UUAC 75-7E, Variable pointer for FOR/NEXT
UUAD-QUAE 77-7fl Y-save; op-save; BASIC pointer
save

UUAT ?q Comparison symbol


accumulator
DD5D-DD55 flD-flS Miscellaneous work area,
pointers, and so on
DD5t-DD5fl flt-flfl Jump vector for functions
DDSq-ODtE aq-qa Miscellaneous numeric work
area

DDb3 qq Accum#1: exponent


00b4-DDb7 1DD-1D3 Accum#1: mantissa
OOtfl IQA Accum#1: sign
DDtq IDS Series evaluation constant
pointer
DDtA-DDtF IDt-lll Accum#2: exponent, and so on
DD7D HE Sign comparison, Acc#1 versus
#2
DD71 113 Accum#1 lo-order (rounding)
DD7E-0Q73 114-115 Cassette buffer len/Series
pointer
D074-DD75 llt-117 Auto line number increment
DD7t llfl Graphics flag
DD77 iiq Color source number
QD7fl-DQ7c1 1ED-1E1 Temporary counters
DD7A-DD7C 1EE-1E4 DS$ descriptor
DD7D-D07E 1E5-I5t BASIC pseudo-stack pointer
13E Multicolor 1 (1)
ODflS 133 Multicolor 2 (2)
APPENDIX C — COMMODORE 128 277

Hex Decimal Description

DDflfc Graphic foreground color (13)


DDRD Status word ST
DDHl 14S Keyswitch IA: STOP and RVS
flags
ODHE Timing constant for tape
14fl Serial output: deferred
character flag
DDRS 14^ Serial deferred character
DDR? 151 Register save
DDRfl 15E How many open files
DDRR 153 Input device, normally 0
DD^A 154 Output CMD device, normally 3
PI n QP nnno
LJ LJ 1 D ^~ l.f ' ' 1 \_r 155-15b Tape parity, output-received
flag
DDSD 157 I/O messages: lc!E = all,
t4 = errors, D = nil
153-15°! Tape error pointers
ltD-lfcE Jiffy Clock HML
DDA3-DDAB Ifc3-171 I/O work bytes
DOAC-DDAD 17E-173 Pointer: tape buffer, scrolling
OOAE-DDAF 174-175 Tape end adds/End of program
QDBD-00B1 17t-177 Tape timing constants
DDB5-DDB3 17fl-17cl Pointer: start of tape buffer
DDB7 Ifl3 Number of characters in file
name
DDBfl 164 Current logical file
DDBq Ifl5 Current secondary address
DDBA 18b Current device
DDBB- ■DDBC Ifl7-lflfl Pointer to file name
DDBD- ■DDC5 iaq-iq7 I/O work pointers
DDCfc- •0DC7 iqa-iqq Banks: I/O data, filename
DDCfl- •DDCB 2DD-203 RS-232 input/output buffer
addresses
DDCC-DOCD 2D4-ED5 Keyboard decode pointer (Bank
15)
DDCE-DDCF 5Dt-5D7 Print string work pointer
DDDD 5Dfi Number of characters in
keyboard buffer
DDD1 Number of programmed chars
waiting
218 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

DDDB 510 Programmed key character


index
00D3 511 Key shift flag: D = no shift
00D4 515 Key code: 88 if no key
DDD5 513 Key code: 88 if no key
DDDb 514 Input from screen/from
keyboard
DDD? 515 40/80 columns: D =40 columns
DDDfl 51b Graphics mode code
517 Character base: 0 = ROM,
4= RAM
OODA-DQDF 516-553 Misc work area
DOED-DDEl 554-555 Pointer to screen line/cursor
QDE5-00E3 S5b-557 Color line pointer
0DE4 55fl Current screen bottom margin
DDES Current screen top margin
DDEb 53D Current screen left margin
DDE? 531 Current screen right margin
DDEfi-DDEq 535-533 Input cursor log (row, column)
DDEA 534 End-of-line for input pointer
DDEB 535 Position of cursor on screen
line
DDEC 53b Row where cursor lives
DDED-DDEE 537-53S Maximum screen lines, columns
DDEF 53=1 Current I/O character
DDFD 54D Previous character printed
DDF1 541 Character color
DDF3 543 Screen reverse flag
0DF4 544 D= direct cursor; else
programmed
DQFS 545 Number of INSERTS
outstanding
OQFb 54b 555 = Auto Insert enabled
DDF7 547 Text mode lockout
DDFfl 54fl D = Scrolling enabled
DDFR 54q Bell disable
DDFA-DDFF 55D-555 Not used
D1DD-O1FF 55fc-511 Processor stack area
01DD-D13E 55t-31fl Tape error log
APPENDIX C — COMMODORE 128 279

Hex Decimal Description

DEOO-OEAD 51E-b7E BASIC input buffer


OEAE-OEAE t74-tat Bank peek subroutine
OEAF-OEBD bfl7-701 Bank poke subroutine
OEBE-OECC 70E-71b Bank compare subroutine
OECD-OEEE 717-73B JSR to another bank
0EE3-0EFB 73q-7b3 JMP to another bank
OEFC-OEFD 7b4-7fc5 Function execute hook

0300-0301 7ta-7bq Error message link


030E-03D3 770-771 BASIC warm start link
0304-0305 77S-773 Crunch BASIC tokens link
030b-0307 774-775 Print tokens link
O3oa-O3oq 77fc-777 Start new BASIC code link
D3DA-030B 77B-77q Get arithmetic element link
030C-030D 780-761 Crunch FE hook
030E-030F 7BE-7B3 List FE hook
0310-0311 784-735 Execute FE hook
031E-0313 78^-787 Unused
0314-0315 7aa-7aq IRQ vector [F Ab 5]
031b-0317 ?qo-7qi Break interrupt vector [BOOB]
D3ia-03iq 7qE-?q3 NMI interrupt vector [FA4 0]
031A-031B 7q4-7q5 OPEN Vector [EFBD]
031C-031D 7qt_7q7 CLOSE vector [Fl a a]
031E-031F 7qa-?qq Set-input vector [FlOb]
03E0-03E1 aoo-aoi Set-output vector [F14C]
03SE-03E3 aoE-ao3 Restore I/O vector [FESb]
03E4-03E5 604-305 Input vector [EFOb]
03Eb-0357 aob-ao? Output vector [EF?q]
03Ea-03Eq aoa-aoq Test-STOP Vector [FbbE]
03EA-03EB aio-an GET vector [EEEB]
03EC-03ED flis-aia Abort I/O vector [FEEE]
03EE-03EF ai4-ai5 Machine Lang Monitor link
0330-0331 ait-fli? LOAD link
033E-0333 aia-aiq SAVE link
0334-0335 aso-aEi Control code (low) link
033b-0337 aEE-a3E High ASCII code link
033a-033q aE4-BE5 ESC sequence link
034A-0353 a4E-asi Keyboard buffer
0354-035D asE-ati Tab stop bits
035E-03bl afcE-ats Line wrap bits
220 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

D3bE-D3bB flbb-fl75 Logical file table


D3tC-D375 fl7fc-flfl5 Device number table
D37b-D37F Secondary address table
D3flD-D3cIE flHt-HEt CHRGET subroutine
D3flt RDE CHRGOT entry
D3CIF-D3AA C1E7-C13a Fetch from RAM Bank 0
D3AB-D3Bb Fetch from RAM Bank 1
D3B7-03BF Fetch from RAM Bank 1
D3CD-D3Cfl qtD-Rtfl Fetch from RAM Bank 0
D3Cq-D3Dl qtq~q77 Fetch from RAM Bank 0
D3DE-D3D4 q7fl-qflO Unused
D3D5 Current BANK for SYS, PEEK
D3EE Graphic/Text backgrounds
D3E3 Graphic/Multi color log
FFDD bSEflD MMU configuration register
FF01-FFD4 t5Efll-t5Efl-4 MMU load config registers
Bank 0:
D4DD-D7E7 1DE4-EDE3 40-column screen memory
D7Ffl-D7FF Sprite identity area (text)
DflDD-DRFF ED4fl-E5tD BASIC pseudo-stack
DfiDC E57E CIA 1 interrupt log
DfiDD E573 CIA 1 timer enabled
DADF-DA17 E575-E5S3 RS-232 work values
DAlfl E5fl^ RS-232 receive pointer
DAiq E5fl5 RS-232 input pointer
DA1A E5flt RS-232 transmit pointer
DA1B E5fl7 RS-232 send pointer
DA1D-DA1F EBflfl-ESHD Sleep countdown;
FFFF = disable
DAED E5RE Keyboard buffer size
DAE1 E5H3 Screen freeze flag
DAEE EBq^i Key repeat: lEfl=all,

DAE3 E5R5 Key repeat timing


DAE4 ESHt Key repeat pause
DAE5 ESR? Graphics/text toggle latch
DAEt ESHfl 40-col cursor mode
DAE7-DAEA 2sqq-EtDE 40-col blink values
DAEB StD3 80-col cursor mode
DAEC EbD4 40-col video $DDlfl image
APPENDIX C — COMMODORE 128 221

Hex Decimal Description

0A5E-0A5F 5b0b-5b07 80-col pages—screen, color


0A40-0A5A 5b54-5b50 40/80 pointer swap $E0-FA
OAbO-OAbD 5bSb-5bb1 40/80 data swap $354-3bl
D&CO 2755 PAT counter
DAC1-0AC4 2753-575b ROM Physical Address Table
DBDD-DBBF 5fllb-3007 Cassette buffer
QCOD-DDFF 3075-35fl3 RS-232 input, output buffers
DEDD-DFFF 35fi4-4015 System sprites (5b-b3)
1000-1001 401b-4105 Programmed key lengths
1DQA-1DFF 41Db-4351 Programmed key definitions
11DD-113D 4355-4400 DOS Command staging area
1131-llbE 4401-44b5 Graphics work area
llbF 44t3 Trace mode: FF = on
1170-1173 4z;b4-44fc7 Renumbering pointers
1174-1177 44tfl-4471 Directory work pointers
1170-1117 Graphics index
117A-117B 4474-447S Float-fixed vector [fl4cIF]
117C-117D ziz;7t-4477 Fixed-float vector [7130]
117E-11D5 447fi-45t5 Sprite motion tables (8x11
bytes)
HDt-llE5 45tt-45fll Sprite X/Y positions
HEb 45fl5 Sprite X-high positions
HE7-llEfl 45fl3-45fl4 Sprite bump masks (sprite,
backgnd)
11E1-11EA 45fl5-45flt Light pen values, X and Y
11EB 45fl7 CHRGEN ROM page, text [Dfl]
11EC 45flfl CHRGEN ROM page, graphics
[DO]
11ED 45flq Secondary address for
RECORD
11EE-11FF 45R0-4t07 Unused
1504-1507 4tl5-4tl5 PU characters ( , .$)
150B-15flC 4tiq-4t50 TRAP address: FFFF if none
1510-1511 4t54-4b55 End of BASIC
1512-1513 4fc5t-4t57 Basic program limit [FF00]
1514-1217 4b5fl-4t31 DO work pointers
iaifl-131A 4t35-4t34 USR program jump [7D5fl]
1B1B-151P 4t35-4t3q RND seed value
1555 4t45 Sound tempo
155F 4t>55 Music sequencer
222 MACHINE LANGUAGE FOR COMMODORE MACHINES

Hex Decimal Description

1234-1237 4bbD-4bb3 Note image


123q-123E 4fcb5-4fc7D Current env pattern
123F-127D 4b71-<72D Envelope tables ..
123F-124fl 4b71-4bflQ AD(SR) pattern
124q-1252 (AD)SR pattern
1253-125C 4bRl-<7DD Waveform pattern
135D-lStt 47D1-471D Pulse width pattern
12b7-127D ^711-472D Pulse width hi pattern
1271-1574 ^721-472^ Note: xx.xx.volume
1275 4725 Previous volume image
127b-127fl ^72t-472fl Collision IRQ task table
1E7H-1E7E 472R-4734 Collision IRQ address tables
127F <735 Collision mask
1260 <73fc Collision work value
12B1 <7fl5 PEN work value
13DD-17FF <fit^-bl43 Unused
iflDD-lBFF Reserved for key functions
1CDD-FBFF 71tfl-t4511 BASIC RAM memory (text)
1C0D-1FF7 71tfl-fllflt Video (color) matrix (hi-res)
IFFfl-lFFF fllfl?-fliqi Sprite identities (hi-res)
2DDD-3FFF fiiq2-lfc3fl3 Screen memory (hi-res)
4D0D-FBFF BASIC RAM memory (hi-res)
Bank 1:
D400-FBFF lD2<-b^511 Basic variables, arrays, strings
Bank 14: Same as Bank 15, below, except:
DDDD-DFFF 5324fl-573^3 Character generator ROM
Bank 15:
40Q0-CFFF lfc3fl4-53247 ROM: BASIC
DDDD-D02E 532<fl-532q^ 40-col video, chip 8564
D4QQ-D41C 54272-5^3DD SID sound chip 6581
D5DD-D5DA 5452fl-5^53fl Memory Management Unit
8722
DtDD-DtDl 547fl^-547fl5 80-column CRT controller 8563
DflDD-DflE7 552qfc-5b2q5 Color nibbles
DCDD-DCDF 5t32D-5fc33t CIA 1 (IRQ) 6526
DDDD-DDDF 5t57b-5t5qi CIA 2 (NMI) 6526
DFDO-DFDfl 57Dflfl-57Dqfl DMA slot
EDDD-FEFF 573^^-t527q ROM: Kernal
FFD5-FFFF t52fl5-t5535 ROM: Transfer, Jump Table
APPENDIX C — COMMODORE 128 223

8502 Processor I/O registers


0000 xxxxx 0 = in 1=out 0 = in 1=out 1=out 1=out 1=out 00000

0001 xxxxx Caps Tape Tape Tape HiRes LoRes Color 00001
Key Motor Sense Outpt Acces

Figure C.15
224 MACHINE LANGUAGE FOR COMMODORE MACHINES

C128 Memory Map


8564 Video — Sprite Registers

Sprite Sprite Sprite Sprite


0 7 0 7
DOOO DOOE 53248 53262
Position
DOO1 DOOF Y 53249 53263

D027 D02E Color 53287 53294

Sprite Bit Positions

7 6 5 4 3 2 1 0

DO1O X-position high 53264

D015 Sprite Enable 53269

D017 Y-expand 53271

D01B Background Priority 53275


D01C Multicolor 53276
DO1D X-expand 53277

DO1E Interrupt: Sprite coilisn 53278


DO1F Interrupt: Sprite/Backgrd coll 53279

Figure C.16
APPENDIX C — COMMODORE 128 225

C128 Memory Map


8564 Video — Control and Misc. Registers
D011 Extnd Color Bit Map | Dsply Enabl | Row Selct A Y-Scroll 53265

D012 Raster Register 53266

D013 X 53267
— Light Pen Input
D014 Y 53268

D016 X Reset Multicolor Colm Sel X-Scroll

D018 Screen (Video Matrix) Character Base 53272


vm13 vm12 vm11 vm10 cb13 . cb12 . cb11 X

SSC j SBC RST 53273


D019 IRQ IRQ sence
LP .
Light Collision
D01A IRQ Enable Pen, Sprt , Back Rastr 53274

COLOR REGISTERS

D020 Exterior 53280

D021 Background #0 53281

D022 Background #1 53282

D023 Background #2 53283

D024 Background #3 53284

D025 Sprite Multicolor #0 53285

D026 Sprite Multicolor #1 53286

D02F xxxxx xxxxx xxxxx xxxxx xxxxx [Keyboard Rows] 53295

D030 xxxxx xxxxx xxxxx xxxxx xxxxx xxxxx Test Fast 53296
Clock

Figure C.16 continued


226 MACHINE LANGUAGE FOR COMMODORE MACHINES

SID (6581) Commodore 128

V1 V2 V3 , V1 V2 V3
D400 D407 D40E L_ 54272 54279 54286
— Frequency
D401 D408 D40F H 54273 54280 54287

D402 D409 D410 Pulse Width L_ 54274 54281 54288

D403 D40A D411 H 54275 54282 54289

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

D416 Filter Frequency H 54294


Resonance Filter voices
D417 i i i 1EXT i v3 i V2 i V1 54295

Passband Master
D418 off, h. .bd.loI Volume, 54296

Filter & Volume


(write only)

D419 Paddle X 54297

D41A Paddle Y 54298


D41B Noise 3 (random) 54299
D41C Envelope 3 54300

Sense
(read only)

Special voice features (TEST, RING MOD, SYNC) are


omitted from the above diagram.

Figure C.17
APPENDIX C — COMMODORE 128 227

Memory Management Unit 8722


0500 RAM select HIGH RAM MID RAM LO CGEN 54528
0-3 /ROM /ROM RAM

D501 Preconfiguration registers; 54529


-D504 Similar to D500, above -54532

D505 40/80 C64 Cartr-Sense Fast xxxxxxxxxxx Z80 54533


Key Mode Color-Bank Disk

D506 Video-Bank xxxxxxxxxxx Shared RAM Shared RAM 54534


hi low 0 = 1K

D507 L Zero page pointer 54535


D508 H ($0000) 54536

D509 L Stack page pointer 54537


D50A H ($0100) 54538

Figure C.18
228 MACHINE LANGUAGE FOR COMMODORE MACHINES

8563 CRT Controller


0600 read (status):

D600 Status Light Vert xxxxxxxxxxxxxxxxxxxxxxxxx 54784


Pen Blank

D600 D601 Typical


54785 Value
54784

0 $00 Horizontal Total 126

1 $01 Horizontal Characters Displayed (80) 80

2 $02 Horizontal Sync position 102

3 $03 Sync Width 1/3


Vertical Horizontal

4 $04 xxxxx Vertical Total 32 or 39

5 $05 xxxxx xxxxx xxxxx Vertical Total Adjust 0

6 $06 xxxxx Vertical Displayed (25) 25

7 $07 xxxxx Vertical Sync Position 29 or 32

8 $08 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Interlace 0

9 $09 xxxxxxxxxxxxxxxxx Scan Lines per Character 7

10 $0A xxxxx Cursor Mode Cursor Start 32

11 SOB xxxxxxxxxxxxxxxxx Cursor End 7

12 $0C xxxxxxxxxxx Display H 0

13 $0D Address L 0

14 $0E Cursor Address H 0


15 $0F L 0

16 $10 Light Pen H varies


17 $11 Input L varies

18 $12 Video RAM Address H varies


19 $13 (See register 31) L varies

20 $14 Color H 8
21 $15 Address L 0

22 $16 Character 120


Total Display Horizontal

23 $17 xxxxxxxxxxxxxxxxx Display Vertical 8

24 $18 Block Scrn Blink V Scroll 32


Copy RVS Rate

25 $19 Bit Color Semi Wide H Scroll 64 or 71


Map Enable Graphic Pixel

26 $1A Color 240


Foreground Background

27 $1B Scroll Control Horizontal 0

28 $1C Char set address RAM xxxxxxxxxxxxxxxx 32

29 $1D xxxxx xxxxx xxxxx Underline Scan Line Count 7

30 $1E Character Count varies

31 $1F Video RAM data (see registers 18,19) varies

32 $20 Block Copy Start H varies


33 $21 Address L varies

34 $22 Display begin 125


35 $23 Enable end 100

36 $24 xxxxxxxxxxxxxxxxxxxxxx | DRAM refresh rate 5

Figured 9
APPENDIX C — COMMODORE 128 229

CIA 1 (IRQ) (6526) Commodore 128

Paddle SEL Joystick 0


A , B R I L , D , U
$DC00 PRA 56320
Keyboard Row Select (inverted)

Joystick 1
$DC01 PRB 56321
Keyboard Column Read

$DC02 $FF — All Output DDRA 56322

$DC03 $00 — All Input DDRB 56323

$DC04 TAL 56324


Timer A
$DC05 TAH 56325

$DC06 TBL 56326



Timer B
$DC07 TBH 56327

$DC0C Serial (shift) Register 56332

$DC0D IRQ xxxxxxxxxxx Flag | S.Reg | xxxxx Tim.B Tim.A 56333

$DC0E SReg -< < Timer A 56334


I/O Load O/S . Toggl Start

$DC0F Timer B 56335


Load O/S Start

Figure C.20
230 MACHINE LANGUAGE FOR COMMODORE MACHINES

CIA 2 (NMI) (6526) Commodore 128

Serial Clock Serial Clock ATN RS-232


$DD00 Video Block PRA 56576
In In Out Out Out Out

DSR CTS DCD* Rl* DTR RTS RS-232


PRB 56577
$DD01 In In In In Out Out

Parallel User Port

In In Out Out Out Out Out Out


$DD02 DDRA 56578
$3F

$DD03 $06 For RS-232 DDRB 56579

$DD04 TAL 56580


Timer A
$DD05 TAH 56581

$DD06 TBL 56582


Timer B
$DD07 TBH 56583

RS-232 Timer Timer


$DD0D ICR 56589
In B , A
Timer
$DD0E CRA 56590
A Start

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

Commodore 64: ROM Detail


This type of ROM memory map is intended primarily for users who want
to "browse" through the inner logic of the computer. It allows a user to
disassemble an area of interest, to see why the computer behaves in a
certain way. With the use of this map, the user will be able to identify
subroutines that are called by the coding under study.

I recommend against using the ROM subroutines as part of your own


programs. They often don't do precisely what you want. They change
locations when you move to a different machine. With rare exceptions,
you can probably write better coding to do the job yourself. Stick with the
kernal jump table: especially $FFDE to output; $FFE4 to get input;
$FFE1 to check the RUN/STOP key; $FFCt and $FFCq to switch
input and output respectively; and $FFCC to restore normal input/output.
They are the same on all Commodore computers.

ADQO: ROM control vectors


ADDC: Keyword action vectors
AD5E: Function vectors
ftDflD: Operator vectors
ADSE: Keywords
A1RE: Error messages
A32fi: Error message vectors
A3b5: Miscellaneous messages
A3SA: Scan stack for FOR/GOSUB
A3Bfi: Move memory
A3FB: Check stack depth
A4Dfi: Check memory space
AA3S: Print "out of memory"
A437: Error routine
A4bq: BREAK entry
Print "ready."
A4flQ: Ready for BASIC
A4RC: Handle new line
A533: Re-chain lines
A5bD: Receive input line
A5?q: Crunch tokens
Abl3: Find BASIC line
Ab42: Perform [NEW]
AbSE: Perform [CLR]
AbflE: Back up text pointer
AtRC: Perform [LIST]
232 MACHINE LANGUAGE FOR COMMODORE MACHINES

A74E: Perform [FOR]


&7ED: Execute statement
fifilD: Perform [RESTORE]
R55C: Break
RflEF: Perform [STOP]
Afl31: Perform [END]
Rfl57: Perform [CONT]
Rfl71: Perform [RUN]
Rflfi3: Perform [GOSUB]
RfifiD: Perform [GOTO]
AflDE: Perform [RETURN]
ftflFfl: Perform [baTa]
RRDb: Scan for next statement
ftqEfl: Perform [IF]
Rq3B: Perform [REM]
aq^B: Perform [ON]
RRbB: (Set fixed point number
aq&5: Perform [LET]
aaflD: Perform [PRINT#]
ARflb: Perform [CMD]
aaaD: Perform [PRINT]
aBlE: Print string from (Y.A)
aB3B: Print format character
aB^D: Bad input routine
aB7B: Perform [GET]
aBa5: Perform [INPUT#]
aBBF: Perform [INPUT]
aBFq: Prompt and input
aCDt: Perform [REaD]
aCFC: Input error messages
aDIE: Perform [NEXT]
aD7fl: Type match check
aDqE: Evaluate expression
aEaa: Constant-pi
aEFl: Evaluate within brackets
aEF7: Check for » ) »
aEFF: Check for comma
aFDfl: Syntax error
AF14: Check range
BFEfl: Search for variable
apa?: Set up FN reference
aFEb: Evaluate [OR]
APPENDIX C — COMMODORE 128 233

AFER: Evaluate [AND]


BDlt: Compare
BDfll: Perform [DIM]
BDflB: Locate variable
B113: Check alphabetic
BUD: Create variable
B1 q A : Array pointer subroutine
BIAS: Value 3E7bfl
B1BE: Float-fixed
B1D1: Set up array
BE4 5: Print "bad subscript"
B E A fl : Print "illegal quantity"
B 3 A C: Compute array size
B37D: Evaluate [FRE]
B3R1: Fixed-float
B3HE: Evaluate [POS]
B3At: Check direct
B3B3: Perform [DEF]
B3E1: Check fn syntax
B3F4: Evaluate [FN]
B4b5: Evaluate [STR$]
BA 7 5 : Calculate string vector
BA&7: Set up string
B4F4 : Make room for string
B 5 51: Garbage collection
B 5 BD : Check salvageability
BbOL: Collect string
Bt3D: Concatenate
B fc 7 A: Build string to memory
BfcA3 : Discard unwanted string
BtDB: Clean descriptor stack
BLEC: Evaluate [CHR$]
B7DD: Evaluate [LEFTS]
B7 5C: Evaluate [RIGHTS]
B7 37: Evaluate [MID$]
B7 tl: Pull string parameters
B77C: Evaluate [LEN]
B 7 fl E : Exit string-mode
B7fiB: Evaluate [ASC]
B7 RB : Input byte parameter
B7AD: Evaluate [VAL]
B7EB : Parameters for POKE/WAIT
234 MACHINE LANGUAGE FOR COMMODORE MACHINES

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

BFED: Evaluate [EXP]


ED43: Series evaluation 1
EDSq: Series evaluation 2
EOR?: Evaluate [RND]
EDFR: Kernal calls with error checking
E15A: Perform [SYS]
E15t: Perform [SAVE]
ElfcS: Perform [VERIFY]
Elbfl: Perform [LOAD]
E1BE: Perform [OPEN]
E1C7: Perform [CLOSE]
E1D4: Parameters for LOAD/SAVE
EEDt: Check default parameters
E2DE: Check for comma
E21S: Parameters for open/close
E2b4 : Evaluate [COS]
ESfcB: Evaluate [SIN]
EEbA: Evaluate [TAN]
E3DE: Evaluate [ATN]
E37B: Warm restart
E3CI<: Initialize
E3A5: CHRGET for zero page
E3BF: Initialize BASIC
E447: Vectors for $3DD
E453: Initialize vectors
EAST: Power-up message
E5DD: Get I/O address
E5D5: Get screen size
E5DA: Put/get row/column
E51fl: Initialize I/O
E5<<: Clear screen
E5tt: Home cursor
ESfcC: Set screen pointers
E5AD: Set I/O defaults
E5B4: Input from keyboard
Efc32: Input from screen
Etfl^: Quote test
EtHl: Set up screen print
EbBb: Advance cursor
EfcED: Retreat cursor
E7D1: Back into previous line
236 MACHINE LANGUAGE FOR COMMODORE MACHINES

E71b: Output to screen


Efl7C: Go to next line
Eflqi: Perform (return)
EflAl: Check line decrement
EflBB: Check line increment
EflCB: Set color code
EflDA: Color code table
EflEA: Scroll screen
EHtB: Open space on screen
ERCfl: Move a screen line
ESED: Synchronize color transfer
ERFO: Set start-of-line
ESFF: Clear screen line
EA13: Print to screen
EAE4 : Synchronize color pointer
EA31: Interrupt-clock, etc.
EAfl7: Read keyboard
EB?^: Keyboard select vectors
EBfll: Keyboard 1-unshifted
EBCE: Keyboard 2-shifted
ECD3: Keyboard 3-"Commodore" shift
EC44: Graphics/text contrl
EC4F: Set graphics/text mode
EC7fl: Keyboard 4
ECBH: Video chip setup
ECE7: Shift/run equivalent
ECFD: Screen In address low
EDDR: Send "talk" to serial bus
EDDC: Send "listen" to serial bus
ED4D: Send to serial bus
EDB2: Serial timeout
EDBR: Send listen S A
EDBE: Clear ATN
EDC7: Send talk S A
EDCC: Wait for clock
EDDD: Send serial deferred
EDEF: Send "untalk" to serial bus
EDFE: Send "unlisten" to serial bus
EE13: Receive from serial bus
EEflS: Serial clock on
EEflE: Serial clock off
EER7: Serial output "1"
EEAD: Serial output "0"
APPENDIX C — COMMODORE 128 237

EEAq: Get serial in and clock signals


EEB3: Delay 1 millisecond
EEBB: RS-232 send
EFDb : Send new RS-232 byte
EFEE: No-DSR error
EF31: No-CTS error
EF3B: Disable timer
EF4 A : Compute bit count
EF5R: RS-232 receive
EF7E : Set up to receive
EFC5: Receive parity error
EFCA : Recieve overflow
EFCD: Receive break
EFDD: Framing error
EFE1: Submit to RS-232
FDDD: No-DSR error
F D17 : Send to RS-232 buffer
F04D: Input from RS-232
FDfib: Get from RS-232
FDA4 : Check serial bus idle
FDBD: Messages
F15B: Print if direct
F13E: Get...
F14E: ...from RS-232
F157: Input
F1 q q : Get: tape/serial/RS-232
F1CA: Output...
F1DD: ...to tape
F5DE: Set input device
F55D : Set output device
P5qi: Close file
F3DF: Find file
F31F: Set file values
F3EF: Abort all files
F333: Restore default I/O
F34A: Do file open
F3D5: Send SA
Open RS-232
Load program
F5 AF: Print "searching"
F5C1: Print filename
F5D2 : Print "loading/verifying"
F5DD: Save program
238 MACHINE LANGUAGE FOR COMMODORE MACHINES

FbflF: Print "saving"


FbRB: Bump clock
FbBC: Log PI A key reading
FfcDD: Get time
FbE4: Set time
FbED: Check stop key
FfcFB: Output error messages
F7ED: Find any tape header
F7bA: Write tape header
F7DD: Get buffer address
F7D7: Set buffer start/end pointers
F7EA: Find specific header
FflDD: Bump tape pointer
Ffll7: Print "press play ..."
FflEE: Check tape status
Ffl3fl: Print "press record ..."
T&A1: Initiate tape read
Ffib<: Initiate tape write
Ffl75: Common tape code
FflDD: Check tape stop
FflEE: Set read timing
FRSC: Read tape bits
FAbD: Store tape characters
FBflE: Reset pointer
FBS7: New character setup
FBAb: Send transition to tape
FBCfl: Write data to tape
FBCD: IRQ entry point
FC57: Write tape leader
FCS3: Restore normal IRQ
FCBfl: Set IRQ vector
FCCA: Kill tape motor
FCD1: Check R/ff pointer
FCDB: Bump R/ff pointer
FCEE: Power reset entry
FDDE: Check fl-ROM
FD1D: fi-ROM mask
FD15: Kernal reset
FDIR: Kernal move
FD3D: Vectors
FD5D: Initialize system constants
FDRB: IRQ vectors
FDA3: Initialize I/O
APPENDIX C — COMMODORE 128 239

FDDD: Enable timer


FDFR: Save filename data
FEOD: Save file details
FED?: Get status
FElfi: Flag status
FE1C: Set status
FE51: Set timeout
FE 2 5 : Read/set top of memory
FEE? : Read top of memory
FEED: Set top of memory
FE34 : Read/set bottom of memory
FE4 3: NMI entry
FEbt: Warm start
FEBt: Reset IRQ and exit
FEBC: Interrupt exit
FE C 5 : RS-232 timing table
FEDt: NMI RS-232 in
FFD?: NMI RS-232 out
FF43: Fake IRQ
FF4fi: IRQ entry
FFfll: Jumbo jump table
F F F A: Hardware vectors
■J
D
Character
Sets

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.

HEX ASCII SCREEN BASIC 6502 DECIMAL


DECIMAL
0 00 @ end-line BRK 0

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

13 OD car ret M ORA 13

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

DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL


30 1E green T ASLX 30
31 1F blue 31
32 20 space space space JSR 32
33 21 I ! ! AND(I.X) 33
34 22 34
35 23 # # # 35
36 24 $ $ $ BITZ 36
37 25 % % % ANDZ 37
38 26 & & & ROLZ 38
39 27 f i 39
40 28 < ( ( PL 40
41 29 > ) ) AND# 41
ROLA 42
* *
2A
*
42
43 2B + + + 43
44 2C • • 1 BIT 44
45 2D - — —
AND 45
46 2E m

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

DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL


79 4F 0 Ho 0 79
80 50 P n,p P BVC 80
81 51 Q 1, q Q EOR(I),Y 81
82 52 R a, r R 82
83 53 S ffl, s S 83
84 54 T E,t T 84
85 55 U a, u U EOR, Z,X 85
86 56 V H, v V LSR Z,X 86
87 57 w g, w w 87
88 58 X SI, x X CLI 88
89 59 Y ri.y Y EORY 89
90 5A z IS, 2 z 90
91 5B [ m [ 91
92 5C \ E \ 92
93 5D ] n ] EORX 93
94 5E T b, a t LSRX 94
95 5F a, s 95
96 60 D RTS 96
97 61 E ADC(I,X) 97
98 62 y 98
99 63 n 99
100 64 □ 100
101 65 D ADCZ 101
102 66 RORZ 102
103 67 □ 103
104 68 q PLA 104
105 69 ADC# 105
106 6A □ RORA 106
107 6B 107
108 6C a JMP(I) 108
109 6D s ADC 109
110 6E a ROR 110
111 6F a 111
112 70 a BVS 112
113 71 a ADC(I),Y 113
114 72 a 114
115 73 m 115
116 74 D 116
117 75 c ADC Z,X 117
118 76 a ROR Z,X 118
119 77 □ 119
120 78 H SEI 120
121 79 U ADCY 121
122 7A a, 0 122
123 7B 123
124 7C a 124
125 7D E ADCX 125 .
126 7E E RORX 126
127 7F s 127
APPENDIX D — SUPERCHART 245

DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL

128 80 r-@ END 128


129 81 orange r-A FOR STA(I.X) 129
130 82 r-B NEXT 130
131 83 r-C DATA 131
132 84 r-D INPUT# STYZ 132
133 85 r-E INPUT STAZ 133
134 86 r-F DIM STXZ 134
135 87 r-G READ 135
136 88 r-H LET DEY 136
137 89 r-l GOTO 137
138 8A r-J RUN TXA 138
139 8B r-K IF 139
140 8C r-L RESTORE STY 140
141 8D car ret r-M GOSUB STA 141
142 8E graphic r-N RETURN STX 142
143 8F bottom r-0 REM 143
144 90 black r-P STOP BCC 144
145 91 cur up r-Q ON STA(I), Y 145
146 92 rvs off r-R WAIT 146
147 93 clear r-S LOAD 147
148 94 insert r-T SAVE STYZ.X 148
149 95 ins. line/br r-U VERIFY STA Z,X 149
150 96 ers. end/p r-V DEF STX Z,Y 150
151 97 Gray 1 r-W POKE 151
152 98 Gray 2 r-X PRINT# TYA 152
153 99 scr. down r-Y PRINT STAY 153
154 9A L Blue r-Z CONT TXS 154
155 9B Gray 3 r-[ LIST 155
156 9C magenta r-\ CLR 156
157 9D cur left M CMD STAX 157
158 9E yellow r-t SYS 158
159 9F cyan r-«- , OPEN 159
160 A0 ■ CLOSE LDY# 160
161 A1 r r-! GET LDA(I,X) 161
162 A2 H r-" NEW LDX# 162
163 A3 □ r-# TAB( 163
164 A4 □ r-$ TO LDYZ 164

165 A5 D r-% FN LDAZ 165


166 A6 r-& SPC( LDXZ 166
167 A7 □ r-' THEN 167

168 A8 H r-( NOT TAY 168


169 A9 V VA r-) STEP LDA# 169
170 AA □ r-* + TAX 170
171 AB OB r- + - 171
172 AC □ r-,
* LDY 172

173 AD r- / LDA 173


174 AE s r-. t LDX 174

175 AF a r-/ AND 175


176 BO a r-0 OR BCS 176
246 MACHINE LANGUAGE FOR COMMODORE MACHINES

DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL


177 B1 ffl r-1 > LDA(I),Y 177
178 B2 e r-2 =
178
179 B3 hd r-3 < 179
180 B4 D r-4 SGN LDY Z,X 180
181 B5 c r-5 INT LDA Z,X 181
182 B6 a r-6 ABS LDX Z,Y 182
183 B7 □ r-7 USR 183
184 B8 H r-8 FRE CLV 184
185 B9 U r-9 POS LDAY 185
186 BA LI, 0 r-: SQR TSX 186
187 BB E RND 187
188 BC a r-< LOG LDYX 188
189 BD r- = EXP LDAX 189
190 BE B r-> COS LDXY 190
191 BF B r-? SIN 191
192 CO H TAN CPY# 192
193 C1 i,a ATN CMP(I),X 193
194 C2 DO, b PEEK 194
195 C3 B,c LEN 195
196 C4 B,d STR$ CPYZ 196
197 C5 H,e VAL CMPZ 197
198 C6 B.f ASC DECZ 198
199 C7 DD,g CHR$ 199
200 C8 DD, h LEFT$ INY 20G
201 C9 D,l RIGHTS CMP# 201
202 CA O.j MID$ DEX 202
203 CB H, k GO 203
204 CC CJ CONCAT CPY 204
205 CD US, m DOPEN CMP 205
206 CE 0, n DCLOSE DEC 206
207 CF Co RECORD 207
208 DO a.P HEADER BNE 208
209 D1 !,q COLLECT CMP(I),Y 209
210 D2 Q,r BACKUP 210
211 D3 1, s COPY 211
212 D4 D,t APPEND 212
213 D5 Q,u DSAVE CMP Z,X 213
214 D6 18, v DLOAD DEC Z,X 214
215 D7 1, w CATALOG 215
216 D8 19, x RENAME CLD 216
217 D9 a.y SCRATCH CMPY 217
218 DA 11,2 DIRECTORY 218
219 DB BB 219
220 DC B 220
221 DD m CMPX 221
222 DE DECX 222
223 DF BJ i 223
224 EO ■ CPX# 224
225 E1 LJI SBC(I),X 225
APPENDIX D — SUPERCHART 247

DECIMAL HEX ASCII SCREEN BASIC 6502 DECIMAL


226 E2 H 226
227 E3 ■ 227
228 E4 ■ CPXZ 228
229 E5 ■ SBCZ 229
230 E6 SB INCZ 230
231 E7 ■ 231
232 E8 n INX 232
233 E9 a, B SBC# 233
234 EA ■ NOP 234
235 EB 0 235
236 EC n CPX 236
237 ED D SBC 237
238 EE 0 INC 238
239 EF 1 239
240 FO c BEQ 240
241 F1 Q SBC(I), Y 241
242 F2 Q 242
243 F3 a 243
244 F4 ■ 244
245 F5 1 SBC Z,X 245
246 F6 V INC Z,X 246
247 F7 i 247
248 F8 B SED 248
249 F9 H SBCY 249
250 FA i,D 250
251 FB 9 251
252 FC B 252
253 FD a SBCX 253
254 FE a INCX 254
255 FF h 255
248 MACHINE LANGUAGE FOR COMMODORE MACHINES

Control Character Representations

NUL Null DLE Data Link Escape (CC)


SOH Start of Heading (CC) DC1 Device Control 1
STX Start of Text (CC) DC2 Device Control 2
ETX End of Text (CC) DC3 Device Control 3
EOT End of Transmission (CC) DC4 Device Control 4
ENQ Enquiry (CC) NAK Negative Acknowledge (CC)
ACK Acknowledge (CC) SYN Synchronous Idle (CC)
BEL Bell ETB End of Transmission Block
BS Backspace (FE) (CC)
HT Horizontal Tabulation CAN Cancel
(FE) EM End of Medium
LF Line Feed (FE) SUB Substitute
VT Vertical Tabulation (FE) ESC Escape
FF Form Feed (FE) FS File Separator (IS)
-* CR Carriage Return (FE) GS Group Separator (IS)
SO Shift Out RS Record Separator (IS)
SI Shift In US Unit Separator (IS)
DEL Delete
(CC) Communication Control
(FE) Format Effector
(IS) Information Separator

Figure D.1

Special Graphic Characters

-> SP Space -*< Less Than


->! Exclamation Point -* = Equals
-»" Quotation Marks -» > Greater Than
-> # Number Sign -> ? Question Mark
-»$ Dollar Sign -> @ Commercial At
-> % Percent ->[ Opening Bracket
-» & Ampersand \ Reverse Slant
->' Apostrophe -^] Closing Bracket
-> ( Opening Parenthesis Circumflex
—>) Closing Parenthesis Underline
-»* Asterisk Grave Accent
-► + Plus { Opening Brace
->, Comma I Vertical Line (This graphic
-> - Hyphen (Minus) is sometimes stylized to
—>. Period (Decimal Point) distinguish it from the un
-> / Slant broken Logical OR which
->: Colon is not an ASCII character)
->; Semicolon } Closing Brace
Tilde

Characters marked correspond to the PET ASCII character set.


Figure D.2
APPENDIX D — ASCII 249

ASCII
ASCII is the American Standard Code for Information Interchange. It is
the standard for communications, and is often used with non-Commodore
printers.

When a Commodore machine is in its graphic mode, its character set


corresponds closely to ASCII. Numeric, upper case alphabetic, and punc
tuation characters are the same. A few control characters, such as
RETURN, also match. Commodore graphics have no counterpart in ASCII.

When the Commodore machine is switched to text mode, the character


set diverges noticeably from ASCII. Numeric characters and much of the
punctuation corresponds, but ASCII upper case alphabetic codes match
the Commodore computer's lower case codes. Commodore's upper case
alphabetics are now completely out of the ASCII range, since ASCII is a
seven-bit code.

As a result, Commodore's PET ASCII codes require conversion before


transmission to a true ASCII device or communications line. This may be
done with either hardware interfacing or with a program. Briefly, the pro
cedure is:

1. If the Commodore character is below $3F, it may be transmitted directly to


the ASCII facility.
2. If the Commodore character is between $4 D and $ 5F, it should be logically
ORed with $2D (or add decimal 32) before transmission to ASCII.
3. If the Commodore character is between $CD and $DF, it should be logically
ANDed with $?F (or subtract decimal 15fl)before transmission to ASCII.

Equivalent rules can be derived to allow a Commodore computer to receive


from ASCII. For either direction of transmission, some control characters
may require special treatment.
250 MACHINE LANGUAGE FOR COMMODORE MACHINES

First Hexadecimal Digit

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

6 ACK SYN & 6 F V f V

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

ASCII code values.


E
Exercises for
Alternative
Commodore
Machines

251
252 MACHINE LANGUAGE FOR COMMODORE MACHINES

Commodore 128 users should turn to page 257.

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

To change the start-of-variables pointer to a location above the machine


language program, display the SOV pointer with .M DD5D 002E and
change the pointer to

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

Plan to start the machine language program at around 1025 + 127, or


1152 (hexadecimal <flO). On that basis, we may change line 130 to
SYS 1152. Do not try to run the program yet.

A 04fl0 LDY #$02


A 04fl2 LDA ($2A),Y
A UA&A STA $033C
A UA&l STA $033E
A 0<flA LDY #$03
A UA&C LDA ($2A),Y
A 04flE STA $033D
A o^qi STA $033F
A o^q^ ASL $033D
A o<q? ROL $033C
A 04RA ASL $033D
A o^qD ROL $033C
A 04A0 CLC
A 0AM LDA $033D
A OAAA ADC $033F
A 04A7 STA $033D
A 0<AA LDA $033C
A O^AD ADC $033E
A Q^BD STA $033C
A 04B3 ASL $033D
A D^Bb ROL $033C
A o^Bq LDY #$02
A O^BB LDA $033C
A 04BE STA ($2A),Y
254 MACHINE LANGUAGE FOR COMMODORE MACHINES

ft 04C0 LDY #$03


ft DACE LDft $D33D
ftOSGB STft ($EA),Y
ft 04C? RTS

To change the start-of-variables pointer to a location above the machine


language program, display the SOV pointer with MOOSAOOSBand
change the pointer to

: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 D33C LDfi $qi


ft D33E STft $1EDD
A0341 JMP ($D3RD)

To place the link address into $03A0/l:

ft 0344 LDft $0314


ft 0347 STft $D3ftD
ft 034A LDft $0315
ft 034t) STfi $03ftl

To fire up the program:

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

To restore the original interrupt:

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)

To place the link address into $03fi0/l:

R U3AA LDR $00R0


R 034? STR $D3RD
R 034A LDR $00=11
R 034D STA $O3R1

To fire up the program:

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

To restore the original interrupt:

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

Project: Adding a Command


PET/CBM Version
It's not possible to write a comparable program to add a command to the
PET/CBM. This machine doesn't have a "link" neatly waiting for us at
address SDBDfi/S. Equivalent code would need to be somewhat longer
and less elegant.

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

Exercises for the Commodore C128


From Chapter 1:
Locations $D3flD and $D3fll are important in the C128. They are sen
sitive BASIC locations, and if we changed them, BASIC would stop work
ing. So we'll change the task to this: swap the contents of locations $ D B fl D
and $DBfll.

Our plan will look like this:


LDA $DBflD (bring in first value)
LDX $DBfll (bring in second value)
STA $DBfll (store in opposite place)
STX $DBflD (and again)
BRK
We may write the machine language code as:
AD flD DB LDA $DBflD
AE fll DB LDX $DBfll
flD fll DB STA $DBfll
flE flD DB STX $DBflD
DD BRK
The code within the computer will consist of 13 bytes: AD flD DB AE fll
DB flD fll DB flE flD DB DD. Now we decide where to put it.

On the Commodore 128, we can't use the addresses starting at $ D 3 3 C—


we'd quickly run into sensitive areas. We'll place our program into the
C128's cassette buffer that starts at address $DBDD (decimal 2 flit). .
That's a good place to put short test programs on this machine, and most
of our following examples will go there.

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.

The information is as explained in chapter 1, except that the address under


PC contains an extra digit. The first digit (in this case, the "F") is called
theTban/c number. The letter F is hexadecimal notation for the value 15
decimal, and we say that the program stopped in bank 15 at address hex

Jjj 380 isl


258 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

To display memory from $lDDDto$lDlDwe would command:


M 1DDD 1D1D
The resulting memory display might look something like this:
>1DDD D7 Db DA D7 Dt UA 05 Dfl:
>lDDfl Dq 05 A? SE Al 50 A& A^: . .GRAPHI
>1D1D A3 AA AC AY Al AA 25 <4:CDLOAD»D
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. The characters to the right of the colon are ASCII
equivalents of the same memory contents (or a period if no equivalent
exists). If you are working on an 80-column monitor, you will get more
memory locations per line than are shown above.

To change memory, we move the cursor until it is positioned over the


memory contents in question, type over the value displayed, and then
press RETURN. You can't change the ASCII part of the display; only the
two-digit hexadecimal values.

If you have displayed the contents of memory, as in the example above,


you might wish to change a number of locations. Be careful: on the Com
modore 128, this particular part of memory holds the definitions for the
function keys, F1 to F10. The first ten bytes are definition lengths; the
following locations, as you might have guessed, are the definitions them
selves. If you play with these locations, expect to end up with muddled
function keys.

Here comes our final rewrite of this program example:


DBDD AD flD DB LDA $DBfiD
DBD3 AE fll DB LDX $0Bfll
OBOb flD fll DB STA $DBfll
DBDq flE flD DB STX $DBflO
DBDC DD
Now to put it in:
APPENDIX E — EXERCISES FOR THE C128 259

We go to the MLM, and display memory with


MBDDBDC
Note that we may drop leading zeros. The first address in full is DDBDD
(bank D, address DBD D). But we may shorten it if we wish; in either case,
we'll get something like:
>DDBDD xx xx xx xx xx xx xx xx:
>DDBDfl xx xx xx xx xx xx xx xx:
Move the cursor back and change this display so that it looks like this:
>DDBDD AD flD DB AE fil DB flD fll:
>DDBDfl DB flE flD DB DD xx xx xx:
Don't type in the "xx" or the trailing colon or periods—just leave whatever
was there before. And be sure to press RETURN. If you have an 80-
column display, you will get the whole memory display on one line.
Now we must put something into locations $DBflO and $ OB fil so that
we'll know that the swap has taken place correctly.

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.

Minor errors that you spot in either an assembly or a disassembly may


be corrected directly on the right-hand side of the assembly or disassembly
listing. In other words, suppose that you had incorrectly coded LD A #$ 5 fl
during the assembly phase; when you perform the disassembly, this line
will show as
DDBDD AR 5fl LDA #$5fl
You recognize that the $58 should be $48; you may move the cursor up—
use cursor home if you wish—and type over the value on the right-hand
side. In this case, you place the cursor over the 5, type A to change the
display to $4fl, and press RETURN. You will see from the display that
the problem has been fixed.

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?

We need to call bank 15 because our program calls CHROUT at address


$FFDE—a ROM routine. And we see ROM in bank 15, not in bank 0. If
we were in bank 0 and made a subroutine call to address $FFDE, we'd
jump to RAM at that address. There's no code at that location in RAM,
and we'd be in trouble.

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.

To switch this program to a BASIC-callable subroutine, we must change


the BRK command at the end to an RTS. Disassemble, if you wish, and
type over the BRK with the characters RTS.

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.

The computer has told us that address $DBDD is Efllb in decimal. If


we had forgotten to ask before leaving the MLM, we could now ask BASIC
with PRINT DEC ("DBDD"); we'd get the same value of Efllb printed.
Now we type BANK 15: SYS Efllt. When we press RETURN, the letter
H will be printed.
Don't forget the reason that we are using BANK 15—it's to make sure
that we reach both our program in RAM (below hex A 0 D D) and the Kernal
ROM (hex 4DDD and above) at the same time to avoid extra "switching"
work. Now we may type NEW and enter a BASIC program:
262 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

Once again, you may set up a BASIC loop program:


1DD BANK 15
110 FOR J = l TO 3
150 SYS Sfllt
13DNEXTJ
To preserve the program within DATA statements, type:
FOR J=5fllt TO E535:PRINT PEEK(J) ; :NEXT J
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:
Ib5 D Iflq IA 11 3E 510 555 532 EEA t
5Dfi 545 m, 72 feq 7t 7fc 7q 13
APPENDIX E — EXERCISES FOR THE C128 263

You may arrange these within data statements:


5D DATA ibS,0,Iflq,14,11,32,210,555,232,254,b
bO DATA 20fi,245,qt,72,bq,7b,?k,?q,13
flD FOE J=2fllb TO 2fl35:READ X:POKE J,X:NEXT J
... and our program continues as before with:
1DD BANK 15
110 FOR J = l TO 3
15D SYS Bfllt
130 NEXT J

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

A DDB1R STA $BbD


A DDB1C LDA #$EB
A DDB1E JSR $FFDE
A D0BE1 JSR $BDD
A D0BE4 TAX
A DDBE5 LDA #$3D
A 0DBE7 JSR $FFDE
A OOBEA TXA
A OOBEB CLC
A DOBEC ADC $BtD
A DOBEF ORA #$3D
A DDB31 JSR $FFDE
A 00B34 LDA #$PD
A 0DB3t JSR $FFDE
A 0DB3q RTS
As a matter of style, you might prefer to type addresses as four digits of
hex ($DBDD rather than $BDD) to remind yourself that you're dealing
with sixteen bits. The dollar sign isn't needed, but 1 suggest you keep it.
When you graduate 1 o a full symbolic assembler, you'll be required to use
the dollar sign ; you might as well keep in practice.

The assembled screen code looks like this:


A DDBlb ED DD DB JSR $DBDD
A ODBiq flD tD DB STA $DBtD
A DDB1C Aq 5B LDA #$EB
A DDB1E ED DE FF JSR $FFD2
A DDBE1 ED DD DB JSR $DBDD
A UUBEA AA TAX
A DDB55 AR 3D LDA #$3D
A DDB2? ED DE FF JSR $FFDE
A DDBSA flA TXA
A DDBEB Ifl CLC
A DDBEC tD bO DB ADC $DBtD
A DDBEF Oq 3D ORA #$3D
A DDB31 ED DE FF JSR $FFDE
A DDB34 Aq DD LDA #$DD
A DDB3t ED DE FF JSR $FFDE
A DDB3q tD RTS
Remember we don't want to SYS to address $BDD (Eflit)—that's the
subroutine. Instead, command BANK 15:SYS 2838 which takes us to the
main routine at $ OB It.
APPENDIX E — EXERCISES FOR THE C128 265

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

a ODBO? at, BC STX $BC


a OOBOq aq 00 LDa #$00
a ODBOB as BB STa $BB
a OOBOD as 00 LDX #$00
a 00B0F ao 04 LDY #$04
a 00B11 Bl BB LDa ($BB),Y
a 00B13 cq SO CMP #$S0
a 00B15 FO OS BEQ $OBiq
a 00B17 4q ao EOR #$ao
a OOBiq qi BB STa ($BB),Y
a 00B1B ca INY
a 00B1C CO IS CPY #$15
a 00B1E qo Fl BCC $OB11
a 00B50 la CLC
a 00B51 as BB LDa $BB
a 00B53 tD ao ob aDC $DB6D
a OOBSt as BB STa $BB
a OOBSa as BC LDa $BC
a ooBsa tq 00 aDC #$00
a 00B5C as BC STa $BC
a 00B5E Efl INX
a 00B5F EO OE CPX #$0E
a 00B31 DO DC BNE $0B0F
a 00B33 bO RTS
Now for the BASIC program to demonstrate how it all works:
100 BANK 15
110 FOR J = 1 to 10
150 sys salt i

130 FOR K to 500


140 NEXT K,J

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

Siting Behind BASIC

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.

On the Commodore 128, a BASIC program usually is stored at address


$1CD1. The address may change if graphics functions are used. That's
quite a high address compared to most other Commodore machines. You
may establish where a program ends by checking addresses $ 121D and
$1211. If you wish to write a program that will be located in bank 0 but
above your BASIC program, the pointer will tell you the area that's safe.
After you have placed your program in the appropriate part of memory,
you may move the pointer up. From that point on, BASIC and machine
language will load and save together as a unit.

We're going to use this parked-behind-BASIC location in the exercise.


There's one more caution, however. If you want the ease of bank 15
operation, with simple access to your program in RAM and the Kernal in
ROM, you must make sure that your programs—BASIC and machine
language together—don't go above address $4ODD. Beyond that point,
you'll have to carefully call banking subroutines—principally the one at
$ D 2 CD—to make sure that everything works together. For our tiny sample
program, that's no problem.

Crossing The Banks

Here's a new problem. Our program—located in bank 0— is going to look


at and change BASIC variables as they lie in memory. How can a program
in bank 0 look at and change data in bank 1 ? We must learn about the
subroutines that allow us to do this.

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

To STORE in any memory bank, we must call subroutine INDSTA (indirect


stash), located at address $FF77. Set things up as if you are about to
do a STA (..),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 storing it into address $O2BCI. Then load the bank number
into register X, load A with the data to be stored, load Y with an appropriate
value for indirect use, and call JSR $FF77. The data will be stored in
the appropriate bank.

Project

Type NEW and enter the BASIC program as follows:


1D0 V£=0:BANK15
110 FOR J = l TO 5
120 INPUT "VALUE";V£
13D SYS + + + +
140 PRINT "TIMES TEN = » ; V%
150 NEXT J
Assuming that our BASIC program will occupy less than 127 bytes and
that BASIC starts at $1CO1 (decimal 7169) we can start our machine
language program at around 7169 + 127, or 7296 (hexadecimal ICflO).
On that basis, we may change line 13D to SYS 75Rb. Do not try to
run the program yet. It would be a good idea to call the monitor briefly
and display the contents of memory address 1210-1511 to confirm
that we are in the right range.

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 DICAb ASL $OBD1


ft DICAq ROL $DBOP
ft DICftC CLC
ft DICftD LDR $DBD1
ft D1CBD ADC $DBD3
ft D1CB3 STA $DBD1
ft OlCBb LDA $DBDD
A DICBR ADC $0BDE
A D1CBC STA $DBDD
A D1CBF ASL $DBD1
A 01CCE ROL $DBDD
A D1CC5 SEI
A OlCCfc LDY #$DE
A DlCCfl LDft #$EF (the indirect address)
A D1CCA STft $0£Bq
ft D1CCD LDX #$D1 (bank 1)
A D1CCF LDA $DBDD
A D1CDE JSR $FF7?
A D1CDS LDY #$D3
A D1CD7 LDA $DBD1
A D1CDA LDX #$D1
ft D1CDC JSR $FF77
ft D1CDF CLI
ft DICED RTS
The assembled code will look like this:
ft OlCflO fiD OE LDY #$DE
ft DICflS 7fl SEI
A DlCfl3 Aq EF LDA #$EF
A DICflS AS Dl LDX #$D1
A DlCfl7 ED 74 FF JSR $FF7<
A DICflA flD DD DB STA $DBDD
A DICflD flD DE DB STA $DBDE
A DIC^D AD D3 LDY #$D3
A DIC^E AR SF LDA #$SF
A DICK AE Dl LDX #$D1
A DlCRt ED 74 FF JSR $FF74
A Sfl CLI
A DICRA flD Dl DB STA $DBD1
A DICRD flD D3 DB STA $DBD3
A D1CAD DE Dl DB ASL $DBD1
A D1CA3 EE DD DB ROL $DBDD
A DICAt DE Dl DB ASL $DBD1
A DICAq EE DD DB ROL $DBDD
270 MACHINE LANGUAGE FOR COMMODORE MACHINES

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).

Project: Adding a Command

CHRGET is now at address $03fl0. TXTPTR is still in zero page, how


ever, at $ 3D. We use coding quite similar to that for the Commodore 64.
272 MACHINE LANGUAGE FOR COMMODORE MACHINES

Here's the "intercept":


A DDBDD LDY #1
A OOBOE LDa ($3D),Y
a 00B04 CMP #$Et
a OOBOb BEQ $BDB
a DDBDfl JMP ($0B5D)
a DDBDB JSR $03flD
a DDBDE LDY #D
a ddbid LDa #$Ea
a D0B1E JSR $FFDE
a 00B1S INY
a OOBlb CPY #+lD
a DDBlfl BCC $B1£
a DDBia LDa #$dd
a DDB1C JSR $FFDE
a DDB1F JMP $B0fl
Here's the link to turn it on:
a OOBEE LDa $030fl
a OOBES STa $B5D
a DOBEfi LDa $D3Dq
a DDBEB STa $B51
a DDBEE LDa #$DD
a DDB3D STa $D3Dfl
a DDB33 LDa #$DB
a DDB35 STa $D3DR
a DDB3B RTS
The assembled code will look like this:
a ddboo ao 01 ldy #$di
a DDBDE Bl 3D LDa ($3D),Y
a ooBD< cq Et cmp #$et
a DDBDb FD D3 BEQ $DBDB
a DDBDfl tC 5D DB JMP ($DB5D)
a DDBDB ED 50 D3 JSR $0360
a DDBOE aO DO LDY #$00
a oobio aq aa LDa #$aa
a DDB1E ED DE FF JSR $FFDE
a 0DB15 Cfl INY
a DOBib cd aa cpy #$oa
a DDBlfl qO Ffl BCC $DB1E
a ODBia aq od LDa #$dd
a D0B1C ED DE FF JSR $FFDE
a DDB1F AC Dfl DB JMP $OBOfl
a ODBEE aD Dfl D3 LDa $030fi
APPENDIX E — EXERCISES FOR THE C128 273

A 00BE5 flD 5D OB STA $0B50


A ODBEfl AD 03 LDA $030^
A DDBEB fiD 51 OB STA $0B51
A OOBEE AS DD LDA #$00
A DDB3D flD Dfl 03 STA $D30fi
A D0B33 AS OB LDA #$DB
A DDB35 flD OS 03 STA $030=1
A DDB3fl to RTS
When you have completed and checked the code return to BASIC. Type
NEW and write the following program:
100 PRINT 3+4:&:PRINT 5+t
110 &
120 PRINT "THAT'S ALL"
Type RUN and you'll get a SYNTAX ERROR in line 100. Now implement
the "ampersand" feature with BANK 15:SYS 2850. Type RUN again. The
ampersand command obediently prints ten asterisks each time it is in
voked. A point of interest: in the two examples in this chapter, bank 0 will
work. Can you see why?

From Chapter 8:
Output Example

To put the message HI on the printer:


100 OPEN 1,A
110 BANK 15:SYS Efllb
120 CLOSE 1
The machine language:
A 00B00 LDX #$01
A 00B02 JSR $FFCCI
A 00B05 LDA #$4fl
A 00B0? JSR $FFDE
A 00B0A LDA #$A<=\
A 00B0C JSR $FFD5
A 00B0F LDA #$0D
A 00B11 JSR $FFD2
A 00B14 JSR $FFCC
A 00B1? RTS
The assembled code:
A 00B00 A2 01 LDX #$01
A 00B05 20 C=\ FF JSR $FFCCI
A 00B05 Aq A& LDA #$<fl
A 00B07 50 D2 FF JSR $FFD5
274 MACHINE LANGUAGE FOR COMMODORE MACHINES

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

Create the file as described in Chapter 8. The BASIC program is also


similar except for the SYS command:
1DD OPEN l,fl,3,»DEMO»
110 BANK 15:SYS Efllt
1E0 CLOSE 1
The machine language program:
A DDBDD LDX #$01
A DOBDE JSR $FFCt
A ODBDS JSR $FFE4
A QDBDfl JSR $FFDE
A DDBOB lda $qo
A DDBOD BEQ $DB05
A ODBOF JSR $FFCC
A 00B1E RTS
The assembled code:
A DDBDD AE 01 LDX #$01
A ODBDE SO Ct FF JSR $FFCt
A 0DB05 EO E4 FF JSR $FFE4
A ODBDfl ED DS FF JSR $FFD5
A ODBDB A5 qo LDA $qo
A DDBDD FO Ft BEQ $DB05
A DDBDF EO CC FF JSR $FFCC
A 0DB1E tO RTS
A File Transfer Program

Here comes BASIC:


1DD PRINT "FILE TRANSFER"
110 INPUT "INPUT FROM (DISK,TAPE)" ; A$
1E0 IF LEFT$(A$,1) = "T" THEN OPEN 1:GOTO
130 IF LEFT$(A$,1)<> "D" GOTO 110
UQ INPUT "DISK FILE NAME" ; N$
150 OPEN l,fl,3/N$
itO INPUT "TO (DISK, TAPE, SCREEN)";B$
170 IF LEFT$(B$,1) = "S» THEN OPEN E,3:GOTO
iflO IF LEFT$(B$,1) = "D" GOTO E10
IRQ IF LEFT$(B$,1)O"T" GOTO ltD
APPENDIX E — EXERCISES FOR THE C128 275

200 IF LEFT$(A$,1)=»T» GOTO


210 INPUT "OUTPUT FILE NAME" ; F$
55D IF LEFT$(B$,1) = "D» THEN OPEN

230 IF LEFT$(B$,1)=»T» THEN OPEN 2,1,1,F$


240 BANK 15:SYS TbflO
250 CLOSE 2: CLOSE 1
The above BASIC program should not take up more than 511 bytes; on
a standard Commodore 128, that means that we'll have clear space for
our machine language program starting at $ IE DO (decimal 7680). We'll
move the end-of-BASIC pointer along, of course (NOT the start-of-vari-
ables), so that our machine language program will save together with
BASIC.
A D1EDD LDX #$01
A DIED? JSR $FFCb
A 01E05 JSR $FFE<
A DIEOfi LDX $qo
A D1E0A PHP
A D1E0B PHA
A D1EDC JSR $FFCC
A D1EDF LDX #$02
A D1E11 JSR $FFCq
A 01EK PLA
A 01E15 JSR $FFD2
A DIElfi JSR $FFCC
A D1E1B PLP
A D1E1C BEQ $1EOO
A 01E1E RTS
The assembled code is:
A D1EDD A2 01 LDX #$01
A 01E02 20 Ct FF JSR $FFCt
A 01ED5 20 Ez; FF JSR $FFE4
A OlEOfl At qo LDX $qo
A D1EDA oa PHP
A D1EDB A& PHA
A D1EDC 20 CC FF JSR $FFCC
A 01EDF A2 02 LDX #$02
A Q1E11 20 cq ff JSR $FFCq
A 01EK bo PLA
A 01E15 20 D2 FF JSR $FFD2
A QlElfl 20 CC FF JSR $FFCC
A D1E1B 2fl PLP
A 01E1C FO E2 BEQ $1EOO
276 MACHINE LANGUAGE FOR COMMODORE MACHINES

a D1E1E £>D RTS


Be sure to move the Start-of-Variables pointer ($DS1D/$DE11) so that
it points at address $1E1F.
F
Floating
Point
Representation

277
278 MACHINE LANGUAGE FOR COMMODORE MACHINES

Packed: 5 bytes (as found in variable or array)

Zero Mantissa (value)


Flag/ 4 bytes
Exponent
High bit represents sign of mantissa

Unpacked: 6 bytes (as found in floating accumulator)

ZF/ Sign

Exponent (High Or
Mantissa—4 Bytes
der
Bit only)

• If exponent = 0, the whole number is zero


• If exponent > $80, the decimal point is to be set as many places to the
right as the exponent exceeds $80.
• Example: Exponent: $83 mantissa: 11000000 ... binary set the point
three positions over: 110.000 ... to give a value of 6.
• If exponent < = $80, the number is a fraction less than 1.

Exercise: Represent +27 in Floating Point


27 decimal = 11011 binary; mantissa = 11011000 ... the point is to be
positioned 5 places in (11011.000 ...) so we get:
Exponent: $85 mantissa: 11011000... binary or D8 Q0 00 00 hexadecimal
To pack, we replace the first bit of the mantissa with a sign bit (0 for
positive) and arrive at:

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.

If a program gives trouble, it should be tested using breakpoint techniques.


The BRK (break) instruction is inserted at several strategic points within
the program. The program stops (or "breaks") at these points, and the
programmer has an opportunity to confirm correct behavior of the program
at selected points. Using this technique, a fault can be pinned down quite
closely.

Occasionally, usually because of bad planning, a program crashes and


the cause of the crash cannot be identified. Worse still, a lengthy program
crashes and the user has forgotten to save a copy of it; the user is then
faced with the task of putting it in all over again.

In such cases, uncrashing techniques are sometimes available to bring


the computer back from limbo. They are never entirely satisfactory, and
should be thought of as a last resort.

The technique differs from computer to computer.

279
280 MACHINE LANGUAGE FOR COMMODORE MACHINES

PET/CBM
Original ROM PETs cannot be uncrashed.

Subsequent models can be uncrashed, though hardware additions are


necessary. The reader should find someone with computer hardware
knowledge to assist in fitting the switches to the computer.

A toggle switch is needed, to be connected to the "diagnostic sense" line


of the parallel user port; that's pin 5 of the PUP. The other side of the
toggle switch should connect to ground (pin IE).
Additionally, a momentary pushbutton is required. This must connect the
reset line of the computer to ground. Technically speaking, it's better to
trigger the input of the computer's power-on reset chip (a 555 one-shot),
using a resistor to guard against accidentally grounding a live circuit.
To uncrash, set the toggle switch to "on" and press the pushbutton; the
machine will come back to life in the machine language monitor. Set the
toggle switch off. There's more work to do.
The computer is still in an unstable state. To correct this, either of two
actions may be taken. You may return to BASIC with . X and immediately
give the command CLR; Alternatively, you may type . ; followed by the
RETURN key.

Whatever investigation or other action is needed should be performed


quickly and the computer reset to its normal state.

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.

Program Supermon is not a monitor; it is a monitor generator that will


make a machine language monitor for you. There's a reason for this.
Supermon finds a likely spot in memory and then plunks the MLM there
so as to fit it into the most suitable place.

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.

Supermon contains the following "essential" commands:

R—to display (and change) registers


M—to display (and change) memory
S—to save memory to disk or tape
L—to load from disk or tape
G—to go to an ML program
X—to exit to BASIC

Supermon also contains the following extra commands:

a—to assemble
D—to disassemble
283
284 MACHINE LANGUAGE FOR COMMODORE MACHINES

Most versions of Supermon (not the "do-it-yourself" below) contain the


following commands. Though not used by this book, they are useful:

F—fills memory with fixed contents:


F iflOO ifiFF DD
H—hunts for a memory pattern:
H DflDD IflDD ED DE FF
T—transfers a block of memory to a new location:
T DflDD DBFF flDDD

A few versions of Supermon contain the command .1 which causes ma


chine language single stepping.

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.

The Supermon generated by this program is a "junior" version (to save


your fingers) but it contains all commands needed for this book.
I DATA Eb,fl,100,0,153,34,147,lfl,Eq,Eq,-30
E DATA Eq,Eq,fl3,fl5,flO,tq,flE,3E,54,5E ,-1b
3 DATA 45,77,7q,7fl,0,4q,fl,110,0,153,-3q
4 DATA 34,17,3E,3E,3E,3E,3E,3E,3E,3E,-50
5 DATA 3E,3E/3E,3E,3E,3E,3E,D,75,fl,-3
t DATA lE0,0,153,34,17,3E,4b,4b,74,73,-4fl
7 DATA 77,3E,bb,fl5,fl4,fl4,tq,flE,7 0,73,-5b
fl DATA bq,7b,bfl,0,10E,fl,13a,0,15fl,4a,-4
q DATA iq4,4 0,5E,51,41,17D,50,53,54,17E,-53
ID DATA iq4,40,5E,5E,41,17D,4q,50,55,41,-E5
II DATA 0,0,0,17 0,17 0,17 0,17 0,17 0,17 0,17 0,-fc4
IE DATA 17 0,17 D, 17 D, 17 0,17 0,17 0,17 0,17 0,17 0,17 0,-Eq
APPENDIX H — A DO-IT-YOURSELF SUPERMON 285

13 DaTR 17D,170,170,17 0,170,170,170,lb5,45,133,-bl


14 DaTa 34,lb5,4b,133,35,lb5,55,133,3b,lb5,-lE
15 DaTa 5k,133,37,lb0,D,lb5,34,£0a,E,iqa,-55
It DBTa 35,lclfl,34,177,34,Z0fi,ba,lbS,34,2Dfl,-34
17 DaTa E,iqa,35,iqa,34,177,34,E40,33,133,-5E
Ifl DaTa 3fl,lfc5,34,EDfl,5,iqfl,35,iqfi,34,177,-bD
iq DaTa 34,E<,101,3b,17D,lb5,3a,101,37,7E, -5b
ED DaTa lb5,55,E0a,E,iqa,5b,iqa,55,104,14 5,-l
El DBTa 55,13fl,7E,lfc5,55,EDfi,E,iqfl,5fc,li:1fl,-l

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

14E DATA EDB, E4a,lt5,3E, 133, iq3,19=5,33,133,194,-43


DATA 7b,134,E5E,D,iq7,4Q,E4D,3,3E,ElD, -fcD
DATA 255,Rb,32,55,E52,D,32,523,551,0, -57
14 5 DATA 14E,17,E,lbE,3,3£,47,E5£,D,7E,-43
141 DATA E0E,E0a,E4q,lb£,3,104,5EJ,£33,b3,lb0,-37
147 DATA 5,74,110,17,E,llD,lt,E,13t,EDfl, -It
14fl DATA E4fc,EDE,E0fl,E37,lbE,E,3E,ED7,E55,EDl,-31
14 q DATA 13,E4D,3D,EDl,3E,E4D,E4 5,3E,EED,E54,-q
15D DATA G,17t.,15,3E,ia,E5E,D,lb4,iq3,13£,-q
151 DATA 1^4,133,1R3,itR,4fl,157,1b,E,E3E , 157, -47
15E DATA lfc,E,E3E,EDfi,Elcl,134,4D,lfcE,D,D,-lD
153 DATA 134,3fl,E4D,4,E3D,3a,E4D,117,ltE,D,-q
154 DATA D,134,Eq,lfc5,3fl,3E,EQ,E53,0,lbb,-4fi
155 DATA 4E,134,41,17D,lflfi,t7,E55,D,lflc1,131,-47
151 DATA E5S,D,3E,iq7,£54,D,E0fl,E£7,lbE,b, -54
157 DATA EE4,3,EDfl,E5,lb4,31,E4D,El,lt5,4E,-fc3
15fl DATA EDl,E3E,lfccl,4fl,17fc,33,3E,ED3,E54,D,-3S
15R DATA EDfl,ED4,3E,ED5,E54,D,EDfl,lclcl,13b,EQfl,-Efl
ltD DATA E35,fc,4E,144,ll,lflfi,fcD,E55,D,lflcl, -15
Itl DATA 54,E55,D,3E,lcl7,E54,Q,EDfl,iai,EDE,-l
itE DATA EDfl,EDcl,E4D,lD,3E,li:1tJ,E54,D,SDfl,171,-51
It3 DATA 3E,iqt,S54,D,EDfl,lbt.,lfc5,4D,lc17,Eq,-15
Ib4 DATA EDfl,lfcD,3E,EE3,E51,D,lfc4,31,E40,4D,-b
Ib5 DATA lfc5,41,E01,157,EDfl,Ek,3E,qcl,E5E,0,-35
Itt DATA 144,lD,15E,EDfl,4,lb5,3D,lfc,lD,7t,-4D
It7 DATA fiD,E5E,D,EDD,EDfl,E5D,lt5,3D,lfc,E4fc, -R
Itfl DATA 114,31,EDfi,3,lfl5,iq4,D,D,14 5,193,-bE
Ifcq DATA 13t,EDfl,E4fl,lt5,3fl,145,iq3,3S,5,E53,-41
17D DATA D,133,iq3,13E,iq4,ltD,t5,3E,flt,E5D,-34
171 DATA D,3E,143,E53,D,3E,lfl3,E51,D,3E,-5t
17E DATA 143,E53,D,7t,iqfl,E53,D,ltfl,3E,ED3,-Eq
173 DATA E54,D,EDa,17,15E,E4D,14,134,Efi,ltt,-t3
174 DATA Eq,EEl,lt,E,a,E3E,134,Eq,ltb,Ea,-tD
175 DATA 4D,qt,EDl,4a,144,3,EDl,71,qb,5t, -3D
17b DATA qt,fc4,E,tq,3,EDa,a,fc4,q,4a,-14
177 DATA 34,tq,51,EDa,a,t4,q,fc4,E,tq,-5D
178 DATA 51,EDfl,a,fc4,q,b4,E,fcq,17q,EDa,-47
17q DATA a,b4,q,D,D,34,ta,51,EDa,14D,-ia
iaD DATA ta,D,D,17,34,fca,51,SDfl,14D,fca,-5
lai DATA 154,lfc,34,ba,51,EDa,a,t4,q,lt,-ED
iaE data 34,ba,5i,Eoa,a,b4,q,qa,iq,iED,-bE
183 DATA ltq,D,D,33,lEq,130,D,D,D,D,-41
184 DATA aq,77,145,14t,134,74,133,157,44,41,-3q
APPENDIX H — A DO-IT-YOURSELF SUPERMON 289

IBS DATA AA,35,40,3b,flq,0,0,flfl,3b,3b,-EE


Iflt DATA 0,D,£fi,13fl,Efl,35,q3,13q,E7,lbl,-10
Ifl? DATA 157,13fi,2q,35,157,13q,Eq,lbl,0,0,-q
Ififl DATA41,E5,174,105,lbfl,E5,35,3b,fl3,27,-b4
DATA 35,3E,,fl3,25,lbl,0,0,Eb,qi,qi,-E4
DATA lb5,105,3b,3L,174,174,lbfl,173,41,0,-3
1R1 DATA 0,lE4,a,0,El,15b,iaq,15b,lb5,105,-E0
IRS DATA 41,fi3,13E,iq,5£,17,lb5,105,35,lb0,--Eb
1R3 DATA Elb,qfl,q0,7E,3fl,qfl,14fl,13b,fl4,bfl,-20
DATA E00,fl4,104,bfl,E3E,14fl,0,0,lfl0,fi,-31
DATA 13E,llb,lfl0,40,110,llb,E^,E04,74,114,-3E
DATA E4E,lb4,13fi,0,0,17D,lbE,lbE,llb,llk,-ll
1R7 DATA llb,114,bfl,lD</17fl/5D,l?fl/Q,D,34f-30
DATA D,D,Et,Et,3fl,3fl,114,ll<,13fc,EDD,-E7
DATA lRt,EDE,3fl,7E,tfl,tfl,ifcE,E00,53,5^,-35
EDD DATA flE,77,71,flfl,7fc,fl3,tfi,^A,fc5,ED4,-5R
E01 DATA a5D,Dfiqi,a5D/D,qt,a5D,D,13<,a5D,-a5
EOE DATA D,EE^,E5D,D,l^,E51,D,llt,E51,D,-E3
ED3 DATA 135,E51,D,1E0,E5E,D,ltD,E53,D,1HA,-1R
ED< DATA E53,D,EEfl,E^q,D,157,E4q,D,13q,E4q,-t3
ED5 DATA D,13,3E,3E,3E,flD,t7,3E,3E,fl3,-3
EDfc DATA flE,3E,fc5,t7,3E,flfl,flE,3E,fiq,flE,-lt
ED7 DATA 3S,fl3,flD,-5q
E55 DATA EDfi
3D0 M=fc3
31D READ X:L = PEEK(M) :H = L = E55:IFH THEN L = X
3ED V = R<)L:S=(T<)fc3 AND R)D AND V)
33D IF V THEN T = L:IF NOT S THEN R = R + 1:S = R<>L
3AU T=(T*3+X)ANDt3
350 IF S THEN PRINT "ERROR LINE";R:E = -1
3tD R = L:IF NOT H GOTO 31D
37D IF E THEN STOP
3fiD PRINT"HERE WE GO" : X= -1: RESTORE : B = EQ<q : FOR A =
to qqqq
3qD IFX) = DTHEN POKEB,X:B = B + 1
ADO READ X:L = PEEK(M) : IF L(E55 THEN NEXT A
410 POKE 45,lfc:POKE 4fc,lt
290 MACHINE LANGUAGE FOR COMMODORE MACHINES

The Commodore 128 Monitor and


SUPERMON +
The built-in machine language monitor of the Commodore 128 is quite
close to SUPERMON. With the release of the C128, a new version of
SUPERMON (called SUPERMON+ ) has been produced so as to match
the commands and syntax of the 128's monitor. Versions are available
for Commodore 64 (including the 128 in 64 mode) and for VIC-20.

The principal features of the new monitor, as compared to SUPERMON,


are as follows:

—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.

Program SUPERMON +, for VIC-20 and Commodore 64, is not a monitor;


it is a monitor generator that will make a machine language monitor for
you. SUPERMON + finds a likely spot in memory and then plunks the
MLM there so as to fit into the most suitable place.

If you are using a VIC-20 or Commodore 64 configuration, load SUPER


MON 4- and type 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 SYSfl.

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

The C128 monitor and SUPERMON + use the following commands:


R—to display (and change) registers
M—to display (and change) memory
S—to save memory to disk or tape
L—to load from disk or tape

G—to go to an ML program

X—to exit to BASIC


A—to assemble
D—to disassemble

The above commands are the only ones used within the text of the book.
Other commands which are available are:

F—fills memory with fixed contents:


F iflOO iflFF DD

H—hunts for a memory pattern:


H DflDD IflDD ED D5 FF
T—transfers a block of memory to a new location:
T DflDD DBFF flODD
C—compares a block of memory to another.
C DflDD DCDD flDDD
J—calls a subroutine directly from the monitor.
J D33C
@—alone, gets the disk status report.
@, $D—gets the disk directory.
@, SD : S AMPLE—sends a command to the disk (in the example, scratch file
SAMPLE).

The @ symbol may be followed by a number to reference a drive other


than unit 8.
■J
I
IA Chip
Information
The following material has been adapted from manufacturer's specifica
tions. The information is not essential to machine language programming,
but can be a great help for further study. Some of these specifications are
not widely published and contain "hard to get" information.

6520 PI A, peripheral interface adaptor


6522 VIA, versatile interface adaptor

6525 TPA, tri port adaptor

6526 CIA, complex interface adaptor

6545 CRTC, CRT controller


6560 VIC video interface chip

6566 VIC-5 video interface chip


6581 SID sound interface chip

[Essentially manufacturer's specs, less hardware details]

293
294 MACHINE LANGUAGE FOR COMMODORE MACHINES

6520 Peripheral Interface Adaptor (PIA)


The 6520 is an I/O device which acts as an interface between the micro
processor and peripherals such as printers, displays, keyboards, etc. The
prime function of the 6520 is to respond to stimulus from each of the two
worlds it is serving. On the one side, the 6520 is interfacing with peripherals
via two eight-bit bi-directional peripheral data ports. On the other side, the
device interfaces with the microprocessor through an eight-bit data bus.
In addition to the lines described above, the 6520 provides four interrupt
input/peripheral control lines and the logic necessary for simple, effective
control of peripheral interrupts.

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 functional configuration of the 6520 is programmed by the micro


processor during systems initialization. Each of the peripheral data lines
is programmed to act as an input or output and each of the four control/
interrupt lines may be programmed for one of four possible control modes.
This allows a high degree of flexibility in the overall operation of the in
terface.

Data Input Register


When the microprocessor writes data into the 6520, the data which ap
pears on the data bus is latched into the Data Input Register. It is then
transferred into one of six internal registers of the 6520. This assures that
the data on the peripheral output lines will not "glitch," i.e., the output lines
will make smooth transitions from high to low or from low to high and the
voltage will remain stable except when it is going to the opposite polarity.
APPENDIX I 295

Control Registers fCR A and CRB)


Figure 1.2 illustrates the bit designation and functions in the Control Reg
isters. The Control Registers allow the microprocessor to control the op
eration of the interrupt lines (CR1, CRE, CB1, CBE), and peripheral
control lines (CRE, CBE). A single bit in each register controls the ad
dressing of the Data Direction Registers (DDRR, DDRB) and the Output
Registers, (ORR, ORB) discussed below. In addition, two bits (bit fc and
7) are provided in each control register to indicate the status of the interrupt
input lines (CR1, CRE, CB1, CBE). These interrupt status bits (IRQR,
IRQB) are normally interrogated by the microprocessor during the inter
rupt service program to determine the source of an active interrupt. These
are the interrupt lines which drive the interrupt input (IRQ, NMI) of the
microprocessor. The other bits in CRR and GRB are described in the
discussion of the interface to the peripheral device.

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.

Data Direction Registers (DDRA, DDRB)


The Data Direction Registers allow the processor to program each line in
the 8-bit Peripheral I/O port to act as either an input or an output. Each
bit in DDRR controls the corresponding lines in the Peripheral A port and
each bit in DDRB controls the corresponding line in the Peripheral B port.
Placing a fl D » in the Data Direction Register causes the corresponding
Peripheral I/O line to act as an input. A •• 1" causes it to act as an output.

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.

Peripheral Output Registers (OR A, ORBJ


The Peripheral Output Registers store the output data which appears on
the Peripheral I/O port. Writing an MD'f into a bit in ORR causes the
corresponding line on the Peripheral A port to go low (< D . A V) if that
line is programmed to act as an output. A11!11 causes the corresponding
output to go high. The lines of the Peripheral B port are controlled by ORB
in the same manner.
296 MACHINE LANGUAGE FOR COMMODORE MACHINES

Interrupt Status Control


The four interrupt/peripheral control lines (CA1, CAE, CB1, CBE) are
controlled by the Interrupt Status Control (A, B). This logic interprets the
contents of the corresponding Control Register, detects active transitions
on the interrupt inputs and performs those operations necessary to assure
proper operation of these four peripheral interface lines.

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.

Interrupt Request Line (I R Q A, IR Q B)


The active low Interrupt Request lines (IRQA and IRQB) act to interrupt
the microprocessor either directly or through external interrupt priority cir
cuitry.

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.

IRQB goes low when CRB-7 = 1 and CRB -D = 1 or when CRB- fc


= 1 and CRB-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.

Peripheral I/O Ports


Each of the Peripheral I/O lines can be programmed to act as an input or
an output. This is accomplished by setting a " 1" in the corresponding
bit in the Data Direction Register for those lines which are to act as outputs.
A " 0 " in a bit of the Data Direction Register causes the corresponding
Peripheral I/O lines to act as an input.

Interrupt Input/Peripheral Control Lines (C Al,


CAE, CB1, CBE
The four interrupt input/peripheral control lines provide a number of special
peripheral control functions. These lines greatly enhance the power of the
two general purpose interface ports (PAD-PA7, PBD-PB7).

Peripheral A Interrupt Input/Peripheral Control


Lines (CM, CAEj
CAl is an interrupt input only. An active transition of the signal on this
input will set bit 7 of the Control Register A to a logic 1. The active transition
can be programmed by the microprocessor by setting a " D " in bit 1 of
298 MACHINE LANGUAGE FOR COMMODORE MACHINES

the CRA if the interrupt flag (bit 7 of CRA) is to be set on a negative


transition of the CA1 signal or a "1" if it is to be set on a positive transition.

Setting the interrupt flag will interrupt the processor through IRQA if bit
D of CR A is a 1 as described previously.

CAE can act as a totally independent interrupt input or as a peripheral


control output. As an input (CRA / bit 5 = D) it acts to set the interrupt
flag, bit b of CRA, to a logic 1 on the active transition selected by bit 4
of CRA.

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.

A second output mode allows CAE to be used in conjunction with CA1


to "handshake" between the processor and the peripheral device. On the
A side, this technique allows positive control of data transfers from the
peripheral device into the microprocessor. The CA1 input signals the
processor that data is available by interrupting the processor. The pro
cessor reads the data and sets CAE low. This signals the peripheral device
that it can make new data available.

The final output mode can be selected by setting bit A of CRA to a 1. In


this mode, CAS is a simple peripheral control output which can be set
high or low by setting bit 3 of CRA to a 1 or a D respectively.

Peripheral B Interrupt Input!Peripheral Control


Lines (CB1, CB2J
CBl operates as an interrupt input only in the same manner as C A1. Bit
7 of CRB is set by the active transition selected by bit D of CRB. Likewise,
the CBE input mode operates exactly the same as the CAE input modes.
The CBE output modes, CRB, bit S = 1, differ somewhat from those
of CAE. The pulse output occurs when the processor writes data into the
Peripheral B Output Register. Also, the "handshaking" operates on data
transfers from the processor into the peripheral device.
APPENDIX I 299

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

CRB IRQB1 IRQB2 CB2 CONTROL DDRB CB2 CONTROL


A ACCESS
f ^

Figure I.2

6545-1 CRT Controller (CRTC)


Concept
The 6545-1 is a CRT Controller intended to provide capability for inter
facing the 6500 microprocessor family to CRT or TV-type raster scan
displays.

Horizontal Total (R0)


This 8-bit register contains the total of displayed and non-displayed char
acters, minus one, per horizontal line. The frequency of HSYNC is thus
determined by this register.

Horizontal Displayed (R1)


This 8-bit register contains the number of displayed characters per hori
zontal line.

Horizontal Sync Position (R2)


This 8-bit register contains the position of the HSYNC on the horizontal
line, in terms of the character location number on the line. The position
of the HSYNC determines the left-to-right location of the displayed text on
the video screen. In this way, the side margins are adjusted.
300 MACHINE LANGUAGE FOR COMMODORE MACHINES

Horizontal and Vertical SYNC Widths (R3)


This 8-bit register contains the widths of both HSYNC and VSYNC, as
follows:

7 6 5 4 3 2 1 0

8 4 2 1

VSYNC WIDTH* HSYNC WIDTH

(NUMBER OF SCAN LINES) (NUMBER OF CHARACTER


CLOCK TIMES)

*IF BITS 4-7 ARE ALL "0"; THEN VSYNC WILL BE 16 SCAN LINES WIDE.

Control of these parameters allows the 6545-1 to be interfaced to a variety


of CRT monitors, since the HSYNC and VSYNC timing signals may be
accommodated without the use of external one-shot timing.

Vertical Total (R4)


The Vertical Total Register is a 7-bit register containing the total number
of character rows in a frame, minus one. This register, along with R5,
determines the overall frame rate, which should be close to the line fre
quency to ensure flicker-free appearance. If the frame time is adjusted to
be longer than the period of the line frequency, then RES may be used
to provide absolute synchronism.

Vertical Total Adjust (R5)


The Vertical Total Adjust Register is a 5-bit write only register containing
the number of additional scan lines needed to complete an entire frame
scan and is intended as a fine adjustment for the video frame time.

Vertical Displayed (R6)


This 7-bit register contains the number of displayed character rows in each
frame. In this way, the vertical size of the displayed text is determined.
>

■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

R4 Vert. Total # Charac. Row y X ■ ■


R5 Vert. Total Adjust. # Scan Lines y XXX
R6 Vert. Displayed # Charac. Rows y X ■ ■
R7 Vert. Sync Position # Charac. Rows y X ■ ■
R8 Mode Control y
R9 Scan Line # Scan Lines y XXX
R10 Cursor Start Scan Line No. y X B, Bo

R11 Cursor End Scan Line No. y XXX


R12 Display Start Addr (H) y X X ■
R13 Display Start Addr (L) y
R14 Cursor Position (H) y y X X
R15 Cursor Position (L) y y
R16 Light Pen Reg.(H) y X X
R17 Light Pen Reg. (L) y
Notes:
■ Designates binary bit
X Designates unused bit. Reading this bit is always "0", except for R31, which does not drive the data bus at
all, and for CS "1" which operates likewise.
302 MACHINE LANGUAGE FOR COMMODORE MACHINES

Vertical Sync Position (R7)


This 7-bit register is used to select the character row time at which the
VSYNC pulse is desired to occur and, thus, is used to position the dis
played text in the vertical direction.

Mode Control (R8)


This register is used to select the operating modes of the 6545-1 and is
outlined as follows:

-INTERFACE MODE CONTROL


BIT
OPERATION
1 0
X 0 NON INTERLACE
X 1 INVALID (DO NOT USE)

"VIDEO DISPLAY RAM ADDRESSING


"0" FOR STRAIGHT BINARY
"1" FOR ROW/COLUMN

-MUST PROGRAM TO "0"

-DISPLAY ENABLE SKEW


"0" FOR NO DELAY
"1" TO DELAY DISPLAY ENABLE
ONE CHARACTER TIME

-CURSOR SKEW
"0" FOR NEW DELAY
"1" TO DELAY CURSOR ONE
CHARACTER TIME

NOT USED

Figure 1.3

Scan Line (R9)


This 5-bit register contains the number of scan lines per character row,
including spacing.
APPENDIX I 303

Cursor Start (R10) and Cursor End (R11)


These 5-bit registers select the starting and ending scan lines for the
cursor. In addition, bits 5 and 6 of R10 are used to select the cursor mode,
as follows:

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.

Display Start Address High (R12) and Low


(R13)
These registers together comprise a 14-bit register whose contents is the
memory address of the first character of the displayed scan (the character
on the top left of the video display, as in Figure 1). Subsequent memory
addresses are generated by the 6545-1 as a result of CCLK input pulses.
Scrolling of the display is accomplished by changing R12 and R13 to the
memory address associated with the first character of the desired line of
text to be displayed first. Entire pages of text may be scrolled or changed
as well via R12 and R13.

Cursor Position High (R14) and Low (R15)


These registers together comprise a 14-bit register whose contents is the
memory address of the current cursor position. When the video display
scan counter (MA lines) matches the contents of this register, and when
the scan line counter (RA lines) falls within the bounds set by R10 and
R11, then the CURSOR output becomes active. Bit 5 of the Mode Control
Register (R8) may be used to delay the CURSOR output by a full CCLK
time to accommodate slow access memories.

LPEN High (R16) and Low (R17)


These registers together comprise a 14-bit register whose contents is the
light pen strobe position, in terms of the video display address at which
304 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

6560 (VIC) Video Interface Chip


The 6560 Video Interface Chip (VIC) is designed for color video graphics
applications such as low cost CRT terminals, biomedical monitors, control
system displays and arcade or home video games. It provides all of the
circuitry necessary for generating color programmable character graphics
with high screen resolution. VIC also incorporates sound effects and A/D
converters for use in a video game environment.

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

• Screen centering capability


• Screen grid size up to 192 Horizontal by 200 Vertical dots
• Two selectable graphic character sizes
• On-chip sound system including:
a) Three independent, programmable tone generators
b) White noise generator
c) Amplitude modulator
• Two on-chip 8 bit A/D converters
• ON-chip DMA and address generation
• No CPU wait states or screen hash during screen refresh
• Interlaced/Non-interlaced switch
• 16 addressable control registers
• Light gun/pen for target tjames

• 2 modes of color operation

A: Interlace mode: A normal video frame is sent to the TV 60 times each


second. Interlace mode cuts the number of repetitions in half. When used
with multiplexing equipment, this allows the VIC picture to be blended with
a picture from another source.
APPENDIX I 305

To turn off: POKE 36864, PEEK(36864) AND 127


To turn on: POKE 36864, PEEK(36864) OR 128

B: Screen origin—horizontal: This determines the positioning of the


image on the TV screen. The normal value is 5. Lowering the value moves
the screen to the left, and increasing it moves the image to the right.

To change value: POKE 36864, PEEK(36864) AND 128 OR X

START VALUE-5K VIC Bit


LOC
Hex Binary Decimal Function

9000 00000101 5 ABBBBBBB

9001 00011001 25 CCCCCCCC

9002 10010110 150 HDDDDDDD

9003 V0101110 46 or 174 GEEEEEEF

9004 vwvvwv V GGGGGGGG


9005 11110000 240 HHHHIIII

9006 00000000 0 JJJJJJJJ

9007 00000000 0 KKKKKKKK

9008 11111111 255 LLLLLLLL

9009 11111111 255 MMMMMMMM

900A 00000000 0 NRRRRRER

900B 00000000 0 OSSSSSSS

900C 00000000 0 PTTTTTTT

900D 00000000 0 QUUUUUUU


900E 00000000 0 wwwwvvw
900F 00011011 27 XXXXYZZZ

A: Interlace mode: 0 = off, N: Bass sound switch


1 = on O: Alto sound switch
B: Screen origin—horizontal P: Soprano sound switch
C: Screen origin—vertical Q: Noise switch
D: Number of video columns R: Bass Frequency
E: Number of video rows S: Alto Frequency
F: Character size: T: Soprano Frequency
0 = 8x8, 1=8x16 U: Noise Frequency
G: Raster value ..V: Loudness of sounds
H: Screen memory location W: Auxiliary color
I: Character memory location X: Screen color
J: Light pen—horizontal Y: Reverse mode 0 = on,
K: Light pen—vertical 1 = off
L: Paddle 1 Z: Border color
M: Paddle 2
306 MACHINE LANGUAGE FOR COMMODORE MACHINES

C: Screen origin—vertical: This determines the up-down placement of


the screen image. The normal value is 25. Lowering this causes the screen
to move up by 2 rows of dots for each number lowered, and raising it
moves the screen down.

To change value: POKE 36865, X

D: Number of video columns: Normally, this is set to 22. Changing this


will change the display accordingly. Numbers over 27 will give a 27 column
screen. The cursor controls are based on a fixed number of 22 columns,
and changing this number makes the cursor controls misbehave.

To change: POKE 36866, PEEK(36866) AND 128 OR X.

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.

To change: POKE 36867, PEEK(36867) AND 129 OR (X*2)

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.

To set 8 by 8 mode: POKE 36867, PEEK(36867) AND 254


To set 8 by 16 mode: POKE 36867, PEEK(36867) OR 1

G: Raster value: This number is used to synchronize the light pen with
the TV picture.

H: Screen memory location: This determines where in memory the VIC


keeps the image of the screen. The highest bit in location 36869 must be
a 1. Bits 4-6 of location 36869 are bits 10-12 of the screen's address,
and bit 7 of location 36866 is bit 9 of the address of the screen. To
determine the location of the screen, use the formula:

S = 4* (PEEK(36866) AND 128) 4- 64* (PEEK(36869) AND 112)

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:

C = 37888 + 4* (PEEK(36866) AND 128)

I: Character memory location: This determines where information on


the shapes of characters are stored. Normally this pointer is to the char
acter generator ROM, which contains both the upper case/graphics or the
APPENDIX I 307

upper/lower case set. However, a simple POKE command can change


this pointer to a RAM location, allowing custom character sets and high
resolution graphics.

To change: POKE 36869, PEEK(36869) AND 240 OR X


(See chart on next page.)

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

0 8000 32768 Upper case normal characters


1 8400 33792 Upper case reversed characters
2 8800 34816 Lower case normal characters
3 8C00 35840 Lower case reversed characters
4 9000 36864 unavailable
5 9400 37888 unavailable
6 9800 38912 VIC chip-unavailable
7 9C00 39936 ROM-unavailable
8 0000 0 unavailable
9 unavailable
10 unavailable
11 unavailable
12 1000 4096 RAM
13 1400 5120 RAM
14 1800 6144 RAM
15 1C00 7168 RAM

L: Paddle X: This contains the digitized value of a variable resistance


(game paddle). The number reads from 0 to 255.

M: Paddle Y: Same as Paddle X, for a second analog input.

N: Bass switch: If this bit is a 0, no sound is played from Voice 1. A 1


in this bit results in a tone determined by Frequency 1.

To turn on: POKE 36874, PEEK(36874) OR 128

To turn off: POKE 36874, PEEK(36874) AND 127

O: Alto switch: See Bass switch.


308 MACHINE LANGUAGE FOR COMMODORE MACHINES

P: Soprano switch: See Bass switch.

Q: Noise switch: See Bass switch.

R: Bass Frequency: This is a value corresponding to the frequency of


the tone being played. The larger the number, the higher the pitch of the
tone.

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:

Register NTSC (US TV's) PAL (European)

36874 3995 4329


36875 7990 8659
36876 15980 17320
36877 31960 34640

To set: POKE 36874, PEEK(36874) AND 128 OR X

S: Alto Frequency: This is a value corresponding to the frequency of the


tone being played. The larger the number, the higher the pitch of the tone.

T: Soprano Frequency: This is a value corresponding to the frequency


of the tone being played. The larger the number, the higher the pitch of
the tone.

To set: POKE 36876, PEEK(36876) AND 128 OR X

U: Noise Frequency: This is a value corresponding to the frequency of


the noise being played. The larger the number, the higher the pitch of the
noise.

To set: POKE 36877, PEEK(36877) AND 128 OR X

V: Loudness of sounds: This is the volume control for all the sounds
playing. 0 is off and 15 is the loudest sound.

To set: POKE 36878, PEEK(36878) AND 240 OR X

W: Auxiliary color: This register holds the color number of the auxiliary
color. The value can be from 0 to 15.
APPENDIX I 309

To set: POKE 36878, PEEK(36878) AND 15 OR (16*X)

X: Screen color: A number from 0 to 15 sets the color of the screen.

To set: POKE 36879, PEEK(36879) AND 15 OR (X*16)


Y: Reverse mode: A 1 in this bit indicates normal characters, and a 0
here causes all characters to be displayed as if reversed.

To turn on reverse mode: POKE 36879, PEEK(36879) AND 247

To turn off reverse mode: POKE 36879, PEEK(36879) OR 8

Z: Border color: A number from 0 to 7 sets the color of the screen.

To set: POKE 36879, PEEK(36879) AND 248 OR X

6522 Versatile Interface Adapter (VIA)


The 6522 Versatile Interface Adapter (VIA) provides two peripheral ports
with input latching, two powerful interval timers, and a serial-to-parallel/
parallel-to-serial shift register.

6522 Versatile Interface Adapter Description

ADDRESS DESCRIPTION REGISTER

9110 PortB AAAAAAAA

9111 Port A (with handshaking) BBBBBBBB

9112 Data Direction B CCCCCCCC

9113 Data Direction A DDDDDDDD

9114 Timer #1, low byte EEEEEEEE

9115 Timer #1, high byte FFFFFFFF

9116 Timer #1, low byte to load GGGGGGGG

9117 Timer #1, high byte to load HHHHHHHH

9118 Timer #2, low byte IIIIIIII

9119 Timer #2, high byte JJJJJJJJ

911A Shift Register KKKKKKKK

911B Auxiliary Control LLMNNNOP

911C Peripheral Control QQQRSSST

911D Interrupt Flags UVWXYZab


911E Interrupt Enable cedfghij
911F Port A (no handshaking) kkkkkkkk
370 MACHINE LANGUAGE FOR COMMODORE MACHINES

PORT A I/O REGISTER

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.

PORT B I/O REGISTER

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.

DATA DIRECTION FOR PORT B

This register is used to control whether a particular bit in PORT B is used


for input or output. Each bit of the data direction register (DDR) is asso
ciated with a bit of port B. If a bit in the DDR is set to 1, the corresponding
bit of the port will be an OUTPUT. If a bit in the DDR is 0, the corresponding
bit of the port will be an INPUT.

For example, if the DDR is set to 7, port B will be set up as follows:

BITS NUMBER DDR PORT B FUNCTION


0 1 OUTPUT
1 1 OUTPUT
2 1 OUTPUT
3 0 INPUT
4 0 INPUT
5 0 INPUT
6 0 INPUT
7 0 INPUT
APPENDIX I _ 377

DATA DIRECTION REGISTER FOR PORT A

This is similar to the DDR for port B, except that it works on PORT A.

E,F,G,H: TIMER CONTROLS

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.

The TIMER locations work differently on reading and writing.

WRITING TO THE TIMER:

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.

F: Read the TIMER T1 high order counter.

G: Read the TIMER T1 low order latch.

H: Read the TIMER T1 high order latch.


372 MACHINE LANGUAGE FOR COMMODORE MACHINES

TIMER T2

This TIMER operates as an interval timer (in one-shot mode), or as a


counter for counting negative pulses on PORT B pin 6. A bit in the ACR
selects which mode TIMER T2 is in.

WRITING TO TIMER T2

I: Write TIMER T2 low order byte of latch.


J: Write TIMER T2 high order counter byte, transfer low order latch to
low order counter, clear TIMER T2 interrupt flag.

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,M,N,O,P: 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

TIMER 2 has 2 modes. If this bit is 0, TIMER 2 acts as an interval timer


in one-shot mode. If this bit is 1, TIMER 2 will count a predetermined
number of pulses on pin PB6.
APPENDIX I 313

N: SHIFT REGISTER 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

1 0 0 FREE RUN MODE AT RATE SET BY TIMER 2


1 0 1 SHIFT OUT UNDER CONTROL OF TIMER 2
1 1 0 SHIFT OUT UNDER CONTROL OF SYSTEM
CLOCK PULSES
1 1 1 SHIFT OUT UNDER CONTROL OF EXTERNAL
CLOCK PULSES

O: PORT B LATCH ENABLE

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.

P: PORT A LATCH ENABLE

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,R,S,T THE PERIPHERAL CONTROL REGISTER

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)

INTERRUPT INPUT MODE:

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.

INDEPENDENT INTERRUPT INPUT MODE:

As above, the CB2 interrupt flag will be set on a negative transition on


the CB2 input line. However, reading or writing to PORT B does not clear
the flag.

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.

INDEPENDENT INPUT MODE:

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.

HANDSHAKE OUTPUT MODE:

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.

PULSE OUTPUT MODE:

The CB2 line is set low for one cycle after a write to PORT B.
APPENDIX I 3t5

MANUAL OUTPUT MODE:

The CB2 line is held low.

MANUAL OUTPUT MODE:

The CB2 line is held high.

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)

INTERRUPT INPUT MODE:

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.

INDEPENDENT INTERRUPT INPUT MODE:

As above, the CA2 interrupt flag will be set on a negative transition on


the CA2 input line. However, reading or writing to PORT A does not clear
the flag.

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

INDEPENDENT INPUT MODE:

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.

HANDSHAKE OUTPUT MODE:

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.

PULSE OUTPUT MODE:

The CA2 line is set low for one cycle after a read or write to PORT A.

MANUAL OUTPUT MODE:

The CA2 line is held low.

MANUAL OUTPUT MODE:

The CA2 line is held high.

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.

UVWXYZab: INTERRUPT FLAG REGISTER

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)

cdefghij: INTERRUPT ENABLE REGISTER

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

6526 (CIA) Complex Interface Adaptor


REGISTER MAP

RS3 RS2 RS1 RSO REG NAME

0 0 0 0 0 PRA PERIPHERAL DATA REG A

0 0 0 1 1 PRB PERIPHERAL DATA REG B

0 0 1 0 2 DDRA DATA DIRECTION REG A

0 0 1 1 3 DDRB DATA DIRECTION REG B

0 1 0 0 4 TALO TIMER A LOW REGISTER

0 1 0 1 5 TAHI TIMER A HIGH REGISTER

0 1 1 0 6 TBLO TIMER B LOW REGISTER

0 1 1 1 7 TBHI TIMER B HIGH REGISTER

1 0 0 0 8 TOD 1Oths 1Oths OF SECONDS REGISTER

1 0 0 1 9 TOD SEC SECONDS REGISTER

1 0 1 0 A TOD MIN MINUTES REGISTER

1 0 1 1 B TODHR HOURS—AM/PM REGISTER

1 1 0 0 C SDR SERIAL DATA REGISTER

1 1 0 1 D ICR INTERRUPT CONTROL REGIS


TER

1 1 1 0 E CRA CONTROL REG A

1 1 1 1 F CRB CONTROL REG B

I/O Ports (PRA, PRB, DDRA, DDRB)

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

Handshaking on data transfej^_can be accomplished using the PC output


pin and the FLAG input pin. PC will go low for one cycle following a read
or write of PORT B. This signal can be used to indicate "data ready" at
PORT B or "data accepted" from PORT B. Handshaking on 16-bit data
transfers (using both PORT A and PORT B) is possible by always reading
APPENDIX I 319

or writing PORT A first. FLAGJs a negative edge sensitive input which


can be used for receiving the PC output from another 6526, or as a general
purpose interrupt input. Any negative transition of FLAG will set the FLAG
interrupt bit.

REG NAME D7 D6 D5 D4 D3 D2 Di Do

0 PRA PA7 PAe PA5 PA4 PA3 PA2 PA! PAo

1 PRB PB7 PB6 PB5 PB4 PB3 PB2 PB, PB0

2 DDRA DPA7 DPAe DPA5 DPA4 DPA3 DPA2 DPA, DPAo

3 DDRB DPB7 DPB6 DPB5 DPB4 DPB3 DPB2 DPB! DPB0

Interval Timers (Timer A, Timer B)

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

A control bit allows the time to be started or stopped by the microprocessor


at any time.

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

A control bit selects the output applied to PORT B. On every timer un


derflow the output can either toggle or generate a single positive pulse of
one cycle duration. The Toggle output is set high whenever the timer is
started and is set low by RES.
320 MACHINE LANGUAGE FOR COMMODORE MACHINES

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

4 TALO TAL7 TAL6 TAL5 TAL4 TAL3 TAL2 TAIh TAL0

5 TAHI TAH7 TAH6 TAH5 TAH4 TAH3 TAH2 TAHt TAH0

6 TBLO TBL7 TBL6 TBL5 TBL4 TBL3 TBL2 TBIh TBI_o

7 TBHI TBH7 TBH6 TBH5 TBH4 TBH3 TBH2 TB^ TBH0

WRITE (PRESCALER)
REG NAME

4 TALO PAL7 PAL6 PAL5 PAU PAL3 PAL2 PAL! PAL0

5 TAHI PAH7 PAH6 PAH5 PAH4 PAH3 PAH2 PAH! PAH0

6 TBLO PB7 PB6 PB5 PB4 PB3 PB2 PBi PB0

7 TBHI PBH7 PBH6 PBH5 PBH4 PBH3 PBH2 PBH! PBH0


APPENDIX I 321

Time of Day Clock (TOD)

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.

A specific sequence of events must be followed for proper setting and


reading of TOD. TOD is automatically stopped whenever a write to the
Hours register occurs. The clock will not start again until after a write to
the 10ths of seconds register. This assures TOD will always start at the
desired time. Since a carry from one stage to the next can occur at any
time with respect to a read operation, a latching function is included to
keep all Time Of Day information constant during a read sequence. All
four TOD registers latch on a read of Hours and remain latched until after
a read of 10ths of seconds. The TOD clock continues to count when the
output registers are latched. If only one register is to be read, there is no
carry problem and the register can be read "on the fly," provided that any
read of Hours is followed by a read of 10ths of seconds to disable the
latching.

READ
REG NAME

8 TOD 0 0 0 0 T8 T4 T2 Ti
10THS

9 TOD 0 SH4 SH2 Shh SL8 SL4 SL2 SLt


SEC

A TOD 0 MH4 MH2 MHi ML8 ML4 ML2


MIN

B TODHR PM 0 0 HH HL8 HL4 HL2 HLt


322 MACHINE LANGUAGE FOR COMMODORE MACHINES

WRITE

CRB7 = 0 TOD
CRB7 = 1 ALARM
(SAME FORMAT AS READ)

Serial Port (SDR)

The serial port is a buffered, 8-bit synchronous shift register system. A


control bit selects input or output mode. In input mode, data on the SP
pin is shifted into the shift register on the rising edge of the signal applied
to the CNT pin. After 8 CNT pulses, the data in the shift register is dumped
into the Serial Data Register and an interrupt is generated. In the output
mode, TIMER A is used for the baud rate generator. Data is shifted out
on the SP pin at 1/2 the underflow rate of TIMER A. The maximum baud
rate possible is <|>2 divided by 4, but the maximum useable baud rate will
be determined by line loading and the speed at which the receiver responds
to input data. Transmission will start following a write to the Serial Data
Register (provided TIMER A is running and in continuous mode). The
clock signal derived from TIMER A appears as an output on the CNT pin.
The data in the Serial Data Register will be loaded into the shift register
then shift out to the SP pin when a CNT pulse occurs. Data shifted out
becomes valid on the falling edge of CNT and remains valid until the next
falling edge. After 8 CNT pulses, an interrupt is generated to indicate more
data can be sent. If the Serial Data Register was loaded with new infor
mation prior to this interrupt, the new data will automatically be loaded
into the shift register and transmission will continue. If the microprocessor
stays one byte ahead of the shift register, transmission will be continuous.
If no further data is to be transmitted, after the 8th CNT pulse, CNT will
return high and SP will remain at the level of the last data bit transmitted.
SDR data is shifted out MSB first and serial input data should also appear
in this format.

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

Interrupt Control (ICR)

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.

The MASK register provides convenient control of individual mask bits.


When writing to the MASK register, if bit 7 (SET/CLEAR) of the data written
is a ZERO, any mask bit written with a one will be cleared, while those
mask bits written with a zero will be unaffected. If bit 7 of the data written
is a ONE, any mask bit written with a one will be set, while those mask
bits written with a zero will be unaffected. In order for an interrupt flag to
set IR and generate an Interrupt Request, the corresponding MASK bit
must be set.

READ (INT DATA)

REG NAME

D | ICR | IR 0 0 FLG SP ALRM TB TA

WRITE (INT MASK)


REG NAME

D | ICR | S/C X X FLG SP ALRM TB TA

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:

Bit Name Function


0 START 1 = START TIMER A, 0 = STOP TIMER A. This
bit is automatically reset when underflow oc
curs during one-shot mode.
1 PBON 1 = TIMER A output appears on PB6, 0 = PB6
normal operation.
2 OUTMODE 1 = TOGGLE, 0 = PULSE
3 RUNMODE 1 = ONE-SHOT, 0 = CONTINUOUS
4 LOAD 1 = FORCE LOAD (this is STROBE input, there
is no data storage, bit 4 will always read back
a zero and writing a zero has no effect).
5 INMODE 1 = TIMER A counts positive CNT transitions,
0 = TIMER A counts 4>2 pulses.
6 SPMODE 1 = SERIAL PORT output (CNT sources shift
clock), 0 = SERIAL PORT input (external shift
clock required).
7 TODIN 1 =50 Hz clock required on TOD pin for ac
curate time, 0 = 60 Hz clock required on TOD
pin for accurate time.
(Bits CRB0-CRB4 are identical to CRA0-CRA4
for TIMER B with the exception that bit 1 con
trols the output of TIMER B on PB7).
5,6 INMODE Bits CRB5 and CRB6 select one of four input
modes for TIMER B as:
CRB6 CRB5
0 0 TIMER B counts 4>2
pulses.
0 1 TIMER B counts positive
CNT transitions.
\ 0 TIMER B counts TIMER
A underflow pulses.
1 1 TIMER B counts TIMER
A underflow pulses while
CNT is high.
ALARM 1= writing to TOD registers sets ALARM,
0 = writing to TOD registers sets TOD clock.
APPENDIX I 325

TOD SP IN RUN OUT


REG NAME IN MODE MODE LOAD MODE MODE PB ON START

E CRA 0 = 60Hz 0= INPUT 0 = 62 1=FORCE 0=CONT 0=PULSE 0 = PB6OFF 0 = STOP


LOAD
1=50Hz 1 = OUT 1=CNT (STROBE) 1=O.S. 1=TOGGLE 1 = PB6 ON 1 = START
PUT

.TA

RUN OUT
REG NAME ALARM IN MODE LOAD MODE MODE PBON START

F CRB 0 = TOD 0 0 = 62 1= FORCE 0 = CONT. 0 = PULSE 0 = PB7OFF 0 = STOP


1 1=CNT LOAD
1 0 = TA
1 = 1 1=CNT-TA (STROBE) 1=O.S. 1=TOGGLE 1=PB7ON 1=START
ALARM

.TB. I

All unused register bits are unaffected by a write and are forced to zero
on a read.

COMMODORE SEMICONDUCTOR GROUP reserves the right to


make changes to any products herein to improve reliability, function
or design. COMMODORE SEMICONDUCTOR GROUP does not
assume any liability arising out of the application or use of any product
or circuit described herein; neither does it convey any license under
its patent rights nor the rights of others.

6566/6567 (VIC-II) Chip Specifications


The 6566/6567 are multi-purpose color video controller devices for use in
both computer video terminals and video game applications. Both devices
contain 47 control registers which are accessed via a standard 8-bit mi
croprocessor bus (65XX) and will access up to 16K of memory for display
information. The various operating modes and options within each mode
are described.

Character Display Mode


In the character display mode, the 6566/6567 fetches CHARACTER
POINTERS from the VIDEO MATRIX area of memory and translates the
pointers to character dot location addresses in the 2048 byte CHARACTER
BASE area of memory. The video matrix is comprised of 1000 consecutive
locations in memory which each contain an eight-bit character pointer.
The location of the video matrix within memory is defined by VM13-VM10
in register 24($18) which are used as the 4 MSB of the video matrix
326 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

CHARACTER POINTER ADDRESS

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

The eight-bit character pointer permits up to 256 different character def


initions to be available simultaneously. Each character is an 8x8 dot
matrix stored in the character base as eight consecutive bytes. The location
of the character base is defined by CB13-CB11 also in register 24 ($18)
which are used for the 3 most significant bits (MSB) of the character base
address. The 11 lower order addresses are formed by the 8-bit character
pointer from the video matrix (D7-D0) which selects a particular character,
and a 3-bit raster counter (RC2-RC0) which selects one of the eight char
acter bytes. The resulting characters are formatted as 25 rows of 40
characters each. In addition to the 8-bit character pointer, a 4-bit COLOR
NYBBLE is associated with each video matrix location (the video matrix
memory must be 12 bits wide) which defines one of sixteen colors for
each character.

CHARACTER DATA ADDRESS

A13 A12 A11 A10 A09 A08 A07 A06 A05 A04 A03 A02 A01 A00

CB13 CB12 CB11 D7 D6 D5 D4 D3 D2 D1 DO RC2 RC1 RC0

Standard Character Mode (MCM = BMM =


ECM = 0)
In the standard character mode, the 8 sequential bytes from the character
base are displayed directly on the 8 lines in each character region. A "0"
bit causes the background #0 color (from register 33 ($21)) to be displayed
while the color selected by the color nybble (foreground) is displayed for
a "1" bit (see Color Code Table).
APPENDIX I 327

CHARACTER
FUNCTION BIT COLOR DISPLAYED

Background 0 Background #0 color


(register 33 ($21))
Foreground 1 Color selected by 4-bit color nybble

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.

Multi-Color Character Mode (MCM = 1,


BMM = ECM = 0)
Multi-color mode provides additional color flexibility allowing up to four
colors within each character but with reduced resolution. The multi-color
mode is selected by setting the MCM bit in register 22 ($16) to "1," which
causes the dot data stored in the character base to be interpreted in a
different manner. If the MSB of the color nybble is a "0," the character
will be displayed as described in standard character mode, allowing the
two modes to be inter-mixed (however, only the lower order 8 colors are
available). When the MSB of the color nybble is a "1" (if MCM:MSB(CM)
= 1) the character bits are interpreted in the multi-color mode:

CHARACTER
FUNCTION BIT PAIR COLOR DISPLAYED

Background 00 Background #0 Color


(register 33 ($21))
Background 01 Background #1 Color
(register 34 ($22))
Foreground 10 Background #2 Color
(register 35 ($23))
Foreground 11 Color specified by 3 LSB of color
nybble

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

Extended Color Mode (ECM = 1, BMM =


MCM = 0)
The extended color mode allows the selection of individual background
colors for each character region with the normal 8x8 character resolution.
This mode is selected by setting the ECM bit of register 17 ($11) to "1."
The character dot data is displayed as in the standard mode (foreground
color determined by the color nybble is displayed for a "1" data bit), but
the 2 MSB of the character pointer are used to select the background
color for each character region as follows:

CHAR. POINTER
MS BIT PAIR BACKGROUND COLOR DISPLAYED FOR 0 BIT

00 Background #0 color (register 33 ($21))


01 Background #1 color (register 34 ($22))
10 Background #2 color (register 35 ($23))
11 Background #3 color (register 36 ($24))

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.

Bit Map Mode


In bit map mode, the 6566/6567 fetches data from memory in a different
fashion, so that a one-to-one correspondence exists between each dis
played dot and a memory bit. The bit map mode provides a screen res
olution of 320H x 200V individually controlled display dots. Bit map mode
is selected by setting the BMM bit in register 17 ($11) to a "1." The VIDEO
MATRIX is still accessed as in character mode, but the video matrix data
is no longer interpreted as character pointers, but rather as color data.
The VIDEO MATRIX COUNTER is then also used as an address to fetch
the dot data for display from the 8000-byte DISPLAY BASE. The display
base address is formed as follows:
APPENDIX I 329

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.

Standard Bit Map Mode (BMM = 1,


MCM = 0)
When standard bit map mode is in use, the color information is derived
only from the data stored in the video matrix (the color nybble is disre
garded). The 8 bits are divided into two 4-bit nybbles which allow two
colors to be independently selected in each 8 x 8 dot block. When a bit
in the display memory is a "0" the color of the output dot is set by the
least significant (lower) nybble (LSN). Similarly, a display memory bit of
"1" selects the output color determined by the MSN (upper nybble).

BIT DISPLAY COLOR


0 Lower nybble of video matrix pointer
1 Upper nybble of video matrix pointer

Multi-Color Bit Map Mode (BMM = MCM

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:

BIT PAIR DISPLAY COLOR


00 Background #0 color (register 33 ($21))
01 Upper nybble of video matrix pointer
10 Lower nybble of video matrix pointer
11 Video matrix color nybble
330 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

Movable Object Blocks


The movable object block (MOB) is a special type of character which can
be displayed at any one position on the screen without the block constraints
inherent in character and bit map mode. Up to 8 unique MOBs can be
displayed simultaneously, each defined by 63 bytes in memory which are
displayed as a 24x21 dot array (shown below). A number of special
features make MOBs especially suited for video graphics and game ap
plications.

MOB DISPLAY BLOCK

BYTE BYTE BYTE

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:

STANDARD MOB (MnMC = 0)

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.

MULTI-COLOR MOB (MnMC = 1)

Each MOB can be individually selected as a multi-color MOB via MnMC


bits in the MOB Multi-color register 28 ($1C). When the MnMC bit is "1,"
the corresponding MOB is displayed in the multi-color mode. In the multi
color mode, the MOB data is interpreted in pairs (similar to the other multi
color modes) as follows:

BIT PAIR COLOR DISPLAYED


00 Transparent
01 MOB Multi-color #0 (register 37 ($25))
10 MOB Color (registers 39-46 ($27-$2E))
11 MOB Multi-color #1 (register 38 ($26))

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

When MOBs are expanded, no increase in resolution is realized. The same


24x21 array (12x21 if multi-colored) is displayed, but the overall MOB
dimension is doubled in the desired direction (the smallest MOB dot may
be up to 4 x standard dot dimension if a MOB is both multi-colored and
expanded).

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:

REG BIT PRIORITY TO CHARACTER OR BIT MAP DATA

0 Non-transparent MOB data will be displayed (MOB in


front)
1 Non-transparent MOB data wil be displayed only in
stead of Bkgd #0 or multi-color bit pair 01 (MOB be
hind)

MOB—DISPLAY DATA PRIORITY

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:

1) A collision between two MOBs occurs when non-transparent output data of


two MOBs are coincident. Coincidence of MOB transparent areas will not
generate a collision. When a collision occurs, the MOB bits (MnM) in the
APPENDIX I 333

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.

MOB Memory Access


The data for each MOB is stored in 63 consecutive bytes of memory. Each
block of MOB data is defined by a MOB pointer, located at the end of the
VIDEO MATRIX. Only 1000 bytes of the video matrix are used in the
normal display modes, allowing the video matrix locations 1016-1023 (VM
base + $3F8 to VM base + $3FF) to be used for MOB pointers 0-7, re
spectively. The eight-bit MOB pointer from the video matrix together with
the six bits from the MOB byte counter (to address 63 bytes) define the
entire 14-bit address field:

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.

BITS REGISTER FUNCTION

X2,X1,X0 22 ($16) Horizontal Position


Y2.Y1 ,Y0 17 ($11) Vertical Position
APPENDIX I 335

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

IRST ERST Set when (raster count) = (stored raster


count)
IMDC EMDC Set by MOB-DATA collision register (first
collision only)
IMMC EMMC Set by MOB-MOB collision register (first
collision only)
ILP ELP Set by negative transition of LP input (once
per frame)
IRQ Set high by latch set and enabled (invert of
IRQ/ output)
336 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

Dynamic Ram Refresh


A dynamic ram refresh controller is built into the 6566/6567 devices. Five
8-bit row addresses are refreshed every raster line. This rate guarantees
a maximum delay of 2.02 ms between the refresh of any single row address
in a 128 refresh scheme. (The maximum delay is 3.66 ms in a 256 address
refresh scheme.) This refresh is totally transparent to the system, since
the refresh occurs during Phase 1 of the system clock. The 6567 generates
both RAS/ and CAS/ which are normally connected directly to the dynamic
rams. RAS/ and CAS/ are generated for every Phase 2 and every video
data access (including refresh) so that external clock generation is not
required.

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.

Certain operations of the 6566/6567 require data at a faster rate than


available by reading only during the Phase 1 time; specifically, the access
of character pointers from the video matrix and the fetch of MOB data.
Therefore, the processor must be disabled and the data accessed during
the Phase 2 clock. This is accomplished via the BA (bus available) signal.
The BA line is normally high but is brought low during Phase 1 to indicate
that the video chip will require a Phase 2 data access. Three Phase-2
times are allowed after BA low for the processor to complete any current
memory accesses. On the fourth Phase 2 after BA low, the AEC signal
will remain low during Phase 2 as the video chip fetches data. The BA
line is normally connected to the RDY input of a 65XX processor. The
character pointer fetches occur every eighth raster line during the display
window and require 40 consecutive Phase 2 accesses to fetch the video
matrix pointers. The MOB data fetches require 4 memory accesses as
follows:

PHASE DATA CONDITION

1 MOB Pointer Every raster


2 MOB Byte 1 Each raster while MOB is displayed
1 MOB Byte 2 Each raster while MOB is displayed
2 MOB Byte 3 Each raster while MOB is displayed

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

NOTE: A dash indicates a no connect. All no connects are read as a '1."

CO
CO
340 MACHINE LANGUAGE FOR COMMODORE MACHINES

COLOR CODES

D4 D3 D1 DO HEX DEC COLOR

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

static address outputs to allow direct connection of these bits to a con


ventional 16K (2K x 8) ROM. (The lower order addresses require external
latching.)

6581 Sound Interface Device (SID) Chip


Specifications
Concept
The 6581 Sound Interface Device (SID) is a single-chip, 3-voice electronic
music synthesizer/sound effects generator compatible with the 65XX and
similar microprocessor families. SID provides wide-range, high-resolution
control of pitch (frequency), tone color (harmonic content), and dynamics
(volume). Specialized control circuitry minimizes software overhead, fa
cilitating use in arcade/home video games and low-cost musical instru
ments.

Features
• 3 TONE OSCILLATORS
Range: 0-4 kHz
APPENDIX I 341

• 4 WAVEFORMS PER OSCILLATOR


Triangle, Sawtooth,
Variable Pulse, Noise
• 3 AMPLITUDE MODULATORS
Range: 48 dB
• 3 ENVELOPE GENERATORS
Exponential response
Attack Rate: 2 ms—8 s
Decay Rate: 6 ms—24 s
Sustain Level: 0—peak volume
Release Rate: 6 ms—24 s

• 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

SID Control Registers


There are 29 eight-bit registers in SID which control the generation of
sound. These registers are either WRITE-only or READ-only and are listed
below in Table 1.

SID Register Description


Voice 1
FREQ LO/FREQ HI (Registers 00,01)

Together these registers form a 16-bit number which linearly controls the
frequency of Oscillator 1. The frequency is determined by the following
equation:

FOut = (Fn x Fclk/16777216)Hz

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:

FOut = (Fn x 0.059604645) Hz

A complete table of values for generating 8 octaves of the equally tempered


musical scale with concert A (440 Hz) tuning is provided in Appendix E.
It should be noted that the frequency resolution of SID is sufficient for any
tuning scale and allows sweeping from note to note (portamento) with no
discernable frequency steps.

PW LO/PW HI (Registers 02,03)

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)%

Where PWn is the 12-bit number in the Pulse Width registers.

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

1 0 0 0 0 1 01 F15 F14 F13 F12 F11 F10 F9 F8 FREQ HI WRITE-ONLY

2 0 0 0 1 0 02 PW7 PW6 PW5 PW4 PW3 PW2 PW1 PW0 PW-LO WRITE-ONLY

3 0 0 0 1 1 03 — — — — PWn PW10 PWg PW8 PWHI WRITE-ONLY

4 0 0 1 0 0 04 NOISE TEST RING SYNC GATE CONTROL REG WRITE-ONLY


n-TL AA AA MOD

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

8 0 1 0 0 0 08 F15 F14 F13 F12 F11 F10 F9 F8 FREQ HI WRITE-ONLY

9 0 1 0 0 1 09 PW7 PW6 PW5 PW4 PW3 PW2 PWi PW0 PWLO WRITE-ONLY

10 0 1 0 1 0 OA — — — — PW11 PW10 PWg PW8 PWHI WRITE-ONLY

11 0 1 0 1 1 OB NOISE TEST RING SYNC GATE CONTROL REG WRITE-ONLY


n_n_ AA AA MOD

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

15 0 1 1 1 1 OF F15 F14 F13 F12 F11 F10 Fg F8 FREQ HI WRITE-ONLY

16 1 0 0 0 0 10 PW7 PW6 PW5 PW4 PW3 PW2 PW! PW0 PWLO WRITE-ONLY

17 1 0 0 0 1 11 — — — — PWn PW10 PWg PW8 PWHI WRITE-ONLY

18 1 0 0 1 0 12 NOISE TEST RING SYNC GATE CONTROL REG WRITE-ONLY


ru-L AA AA MOD

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.

CONTROL REGISTER (Register 04)

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.

Varying the frequency of Oscillator 1 with respect to Oscillator 3 produces


a wide range of complex harmonic structures from Voice 1 at the frequency
of Oscillator 3. In order for sync to occur, Oscillator 3 must be set to some
frequency other than zero but preferably lower than the frequency of
Oscillator 1. No other parameters of Voice 3 have any effect on sync.

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.

One of the output waveforms must be selected for Oscillator 1 to be au


dible, however, it is NOT necessary to de-select waveforms to silence the
output of Voice 1. The amplitude of Voice 1 at the final output is a function
of the Envelope Generator only.

NOTE: The oscillator output waveforms are NOT additive. If more


than one output waveform is selected simultaneously, the result will
be a logical ANDing of the waveforms. Although this technique can
be used to generate additional waveforms beyond the four listed
above, it must be used with care. If any other waveform is selected
while Noise is on, the Noise output can "lock up." If this occurs, the
Noise output will remain silent until reset by the TEST bit or by
bringing RES (pin 5) low.

ATTACK/DECAY (Register 05)

Bits 4-7 of this register (ATK0-ATK3) select 1 of 16 ATTACK rates for


the Voice 1 Envelope Generator. The ATTACK rate determines how rapidly
the output of Voice 1 rises from zero to peak amplitude when the Envelope
Generator is Gated. The 16 ATTACK rates are listed in Table 2.
APPENDIX I 347

Bits 0-3 (DCYO—DCY3) select 1 of 16 DECAY rates for the Envelope


Generator. The DECAY cycle follows the ATTACK cycle and the DECAY
rate determines how rapidly the output falls from the peak amplitude to
the selected SUSTAIN level. The 16 DECAY rates are listed in Table 2.

SUSTAIN/RELEASE (Register 06)

Bits 4-7 of this register (STNO—STN3) select 1 of 16 SUSTAIN levels


for the Envelope Generator. The SUSTAIN cycle follows the DECAY cycle
and the output of Voice 1 will remain at the selected SUSTAIN amplitude
as long as the Gate bit remains set. The SUSTAIN levels range from zero

Table 2. Envelope Rates

VALUE ATTACK RATE DECAY/RELEASE


RATE

DEC (HEX) (Time/Cycle) (Time/Cycle)

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

to peak amplitude in 16 linear steps, with a SUSTAIN value of 0 selecting


zero amplitude and a SUSTAIN value of 15 ($F) selecting the peak am
plitude. A SUSTAIN value of 8 would cause Voice 1 to SUSTAIN at an
amplitude one-half the peak amplitude reached by the ATTACK cycle.

Bits 0-3 (RLS0-RLS3) select 1 of 16 RELEASE rates for the Envelope


Generator. The RELEASE cycle follows the SUSTAIN cycle when the
Gate bit is reset to zero. At this time, the output of Voice 1 will fall from
the SUSTAIN amplitude to zero amplitude at the selected RELEASE rate.
The 16 RELEASE rates are identical to the DECAY rates.

NOTE: The cycling of the Envelope Generator can be altered at any


point via the Gate bit. The Envelope Generator can be Gated and
Released without restriction. For example, if the Gate bit is reset
before the envelope has finished the ATTACK cycle, the RELEASE
cycle will immediately begin, starting from whatever amplitude had
been reached. If the envelope is then gated again (before the RE
LEASE cycle has reached zero amplitude), another ATTACK cycle
will begin, starting from whatever amplitude had been reached. This
technique can be used to generate complex amplitude envelopes
via real-time software control.

Voice 2
Registers 07-$0D control Voice 2 and are functionally identical to registers
00-06 with these exceptions:

1) When selected, SYNC synchronizes Oscillator 2 with Oscillator 1.


2) When selected, RING MOD replaces the Triangle output of Oscillator 2 with
the ring modulated combination of Oscillators 2 and 1.

Voice 3
Registers $0E-$14 control Voice 3 and are functionally identical to reg
isters 00-06 with these exceptions:

1) When selected, SYNC synchronizes Oscillator 3 with Oscillator 2.


2) When selected, RING MOD replaces the Triangle output of Oscillator 3 with
the ring modulated combination of Oscillators 3 and 2.

Typical operation of a voice consists of selecting the desired parameters:


frequency, waveform, effects (SYNC, RING MOD) and envelope rates,
then gating the voice whenever the sound is desired. The sound can be
sustained for any length of time and terminated by clearing the Gate bit.
APPENDIX I 349

Each voice can be used separately, with independent parameters and


gating, or in unison to create a single, powerful voice. When used in unison,
a slight detuning of each oscillator or tuning to musical intervals creates
a rich, animated sound.

Filter
FC LO/FC HI (Registers $15,$ 16)

Together these registers form an 11-bit number (bits 3-7 of FC LO are


not used) which linearly controls the Cutoff (or Center) Frequency of the
programmable Filter. The approximate Cutoff Frequency ranges from 30
Hz to 12 KHz.

RES/FILT (Register $17)

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.

FILT 2 (Bit 1): Same as bit 0 for Voice 2.

FILT 3 (Bit 2): Same as bit 0 for Voice 3.

FILTEX (Bit 3): Same as bit 0 for External audio input (pin 26).

MODE VOL (Register $18)

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)

Same as POTX for the pot tied to POTY (pin 23).


APPENDIX I . 351

OSC 3 RANDOM (Register $1B)

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).

ENV 3 (Register $1C)

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 Tri-Port Interface


Concept
The 6525 TRI-PORT Interface (TPI) is designed to simplify the imple
mentation of complex I/O operations in microcomputer systems. It com
bines two dedicated 8-bit I/O ports with a third 8-bit port programmable
for either normal I/O operations or priority interrupt/handshaking control.
Depending on the mode selected, the 6525 can provide 24 individually
programmable I/O lines or 16 I/O lines, 2 handshake lines and 5 priority
interrupt inputs.

6525 Addressing

6525 REGISTERS/(Direct Addressing)

000 R0 PRA—Port Register A


001 R1 PRB—Port Register B
010 R2 PRC—Port Register C
011 R3 DDRA—Data Direction Register A
100 R4 DDRB—Data Direction Register B
101 R5 DDRC—Data Direction Register C/lnterrupt Mask Register
110 R6 CR—Control Register
111 R7 AIR-—Active Interrupt Register
*NOTE: RS2, RS1, RSO respectively

6525 Control Registers

CR CB, CB0 CA, CA0 IE4 IE3 IP MC

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, CB Functional Description


The CA, CB lines are outputs used in the same fashion as the CA2 and
CB2 output of the 6520.
APPENDIX 1 353

CA Output Modes

CAt CA0 MODE DESCRIPTION

"Handshake" CA is set high on an active transition of the l3


on Read interrupt input signal and set low by a micropro
cessor "Read A Data" operation. This allows
positive control of data transfers from the pe
ripheral device to the microprocessor.

Pulse Output CA goes low for IMS after a "Read A Data" op


eration. This pulse can be used to signal the
peripheral device that data was taken.

0 Manual CA set low.


Output

1 Manual CA set high.


Output

CB Output Modes

MODE DESCRIPTION

"Handshake" CB is set low on microprocessor "Write B Data"


on Write operation and is set high by an active transition
of the l4 interrupt input signal. This allows positive
control of data transfers from the microprocessor
to the peripheral device.

Pulse Output CB goes low for IMS after a microprocessor "Write


B Data" operation. This can be used to signal
the peripheral device that data is available.

0 Manual CB set low.


Output

1 Manual CB set high.


Output

INTERRUPT MASK REGISTER 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 DESCRIPTION

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:

PC7 PC6 PC5 PC4 PC3 PC2 PC, PCo

PRC When MC = 1

CB CA IRQ U i3 l2 l1 lo

INTERRUPT EDGE CONTROL

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) = 0 then l4 (l3) latch will be set on a negative transition of l4


(l3) input.

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.

Interrupt Latch Register


Clears on Read of AIR Using Following
u U >2 li lo Equation

ILR «- ILR © AIR

Active Interrupt Register


A4 A3 A2 A, Ao Clears on Write to AIR

Interrupt Priority Select


IP IP = 0 No Priority

IP = 1 Interrupts Prioritized
APPENDIX I 355

FUNCTIONAL DESCRIPTION

1. IP = 0 No Priority

All interrupt information latched into interrupt latch register_([LR) is im


mediately transferred into activejnterrupt register (AIR) and IRQ is pulled
low. Upon read of interrupt the IRQ is reset high and the appropriate bit(s)
of the interrupt latch register is cleared by exclusive OR-ing. The ILR with
AIR (ILR0AIR). After the appropriate interrupt request has been serviced
a Write to the AIR will clear it and initiate a new interrupt sequence if any
interrupts were received during previous interrupt servicing. In this non-
prioritized mode it is possible for two or more interrupts to occur simul
taneously and be transferred to the AIR. If this occurs it is a software effort
to recognize this and act accordingly.

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

4. Processor recognizes IRQ and reads AIR to determine which


interrupt occurred.
5. Bit \, is reset and IRQ is reset high.
6. Processor begins servicing ^ interrupt and the l2 interrupt is re
ceived.
7. A2 is set, A1 is reset low and IRQ is pulled low.
8. Processor has not yet completed servicing I, interrupt so this
routine will be automatically stacked in 6500 stack queue when
new IRQ for l2 of interrupt is received.
9. Processor reads AIR to determine l2 interrupt occurrence and bit
l2 of interrupt latch is reset.
10. Processor services l2 interrupt, clears A2 by writing AIR and re
turns from interrupt. Returning from interrupt causes 650X pro
cessor to resume servicing ^ interrupt.
11. Upon clearing A2 bit in AIR, the A^ bit will not be restored to a
one. Internal circuitry will prevent a lower priority interrupt from
interrupting the resumed l1a

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.

This is achieved by the use of an Interrupt Stack. This stack is pushed


whenever a read of AIR occurs and is pulled whenever a write to
AIR occurs. It is therefore important not to perform any extraneous
reads or writes to AIR since this will cause extra and unwanted stack
operations to occur.

The only time a read of AIR should occur is to respond to an interrupt


request.

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:

SUPERM0N1 (for original ROM PET computers)


SUPERM0N5 (for upgrade ROM PET/CBM computers)
SUPERM0N4 (for 4.0 PET/CBM computers)
SUPERMON. V (for VIC-20 computers)
SUPERM0Nt4 (for Commodore 64 computers)
SUPERMON INST(instructions, BASIC)
SUPERMON + PET (for upgrade and 4.0 PET/CBM)
SUPERMON + VIC (for VIC-20 computers)
SUPERMON + b4(for Commodore 64 computers)
SUPERMON + INST (instructions, Basic)
UNIC0PYb4 (for Commodore 64)
UNICOPYINST (instructions, BASIC)
UNICOPY LIST (BASIC, all machines)
JUNICOPY ASSY (data file for UNICOPY LIST)
COPY-ftLL (for PET/CBM)
COPY - ALL. b< (for Commodore 64)
CROSS REF (for PET/CBM)
CROSS RE Fb4 (for Commodore 64)
CROSS REF 12 fl (for Commodore 128)
FACTORS (for PET/CBM)
357
358 MACHINE LANGUAGE FOR COMMODORE MACHINES

FACTORS Vt4 (for VIC-20, Commodore 64, and Plus-4)


FACTORS lEfl (for Commodore 128)
PENTOMINOS INST (instructions)
PENTOMINOS (BASIC, all machines)
PENTOMINOS PET (for PET/CBM)
PENTOMINOS Vb4 (for VIC-20, Commodore 64, and Plus-4)
PENTOMINOS lEfl (for Commodore 128)
PENTOMINOS BlEfl (boot for B128 system)
+ PENT01Efl (program for B128)
+ XFER (transfer sequence for B128)
STRING THING (BASIC, for PET/CBM)
STRING THING Vb4 (BASIC, for VIC-20, Commodore 64)
STRING THING lEfl (for Commodore 128)
]SAMPLE FILE (for use with STRING THING)

These programs are public domain, and may be obtained from user groups.
They are available here for user convenience.

The following notes may be useful in using or studying the programs.

SUPERMON1 (for original ROM PET computers)


SUPERMONE (for upgrade ROM PET/CBM computers)
SUPERM0N4 (for 4.0 PET/CBM computers)
SOPERMON. V (for VIC-20 computers)
SUPERMONb4 (for Commodore 64 computers)
SUPERMON INST (instructions, BASIC)

Supermon 2 and 4 are "extensions" to the built-in MLM of the respective


machines. The other Supermon versions are complete monitors. These
are the "original" Supermon programs.

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

SUPERMON + PET (for upgrade and 4.0 PET/CBM)


SUPERMON + VIC (for VIC-20 computers)
SUPERMON + b4 (for Commodore 64 computers)
SUPERMON + INST (instructions, Basic)

A revised version of SOPERMON; the commands closely correspond to


those of the built-in monitors of the Plus-4 and Commodore 128. Contains
a number of convenience features not found in the original SUPER
MON.

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

A BASIC program explaining how to use UNICOPYb4.

UNICOPY LIST
JUNICOPY ASSY

An assembly listing of program UNICOPY. Because UNICOPY is written


entirely in machine language, a number of tasks are performed in the
program that are often more conveniently done in BASIC. For example,
files are opened and closed by machine language. This makes the program
listing particularly interesting for students of these techniques.

Assembly listings have a somewhat different appearance from the machine


language programs this book has dealt with. The most visible difference
is in the use of symbolic addresses. If there is any confusion, concentrate
on the machine language half of the listing; that will clarify what's going
on. Program UNICOPY LIST allows output to the screen or to a Com
modore printer.

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.

COPY-ALL (for PET/CBM)


COPY - ALL. bA (for Commodore 64)
360 MACHINE LANGUAGE FOR COMMODORE MACHINES

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.

CROSS REF (for PET/CBM)


CROSS REF b4 (for Commodore 64)
CROSS REF 15fl (for Commodore 128)

This program prepares a cross-reference listing for any selected BASIC


program on disk. It cross-references both line numbers and variables. It's
a good way to document a BASIC program.

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.

FACTORS (for PET/CBM)


FACTORS Vb4 (for VIC-20, Commodore 64, and Plus-4)
FACTORS lEfl (for Commodore 128)

This program finds factors of numbers up to nineteen digits long. This


shows a powerful feature of machine language as opposed to BASIC: the
size of numbers is not limited by the language.

The program contains a number of useful techniques worth studying. First,


it allows a decimal input of any number up to 19 digits (a 64-bit or 8-byte
binary number). Second, to find factors it performs division with remainder.
Finally, to print results, it must convert binary-to-decimal, using the same
decimal mode technique as in CROSS REF.

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.

The machine language program is relocated by BASIC so that it starts at


hexadecimal 13 DD (in the C128 version, 1DDD) regardless of where it
APPENDIX J 361

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.

Students wishing to disassemble FACTORS will find the following infor


mation useful:

VARIABLES (see note for C-128):


$D34R—number of times a factor divides evenly
$D34 A—"equals" or "asterisk" character for formatting
$D34B—zero suppression flag
$ D 3 A C—30—counter
$D35D to $0357—value under analysis
$D35fl to $D35F—value work area
$D3tD to $D3b7—"base" value for 30-counter
$D3bC to $D37R—division work area, including:
$D3tC to $D3tF—remainder
$D37D to $D377—quotient

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.

PROGRAM (see note for C128):

$13DD: Main routine, including:


$ 13 D D: Start, clear work area
$13ID: Get number digits from user
$1331: Handle bad input
$133 A: Begin factoring; check non-zero
$13 5 D: Try divisors 2, 3, and 5
$1315: Try higher divisors
$13AE: Print remaining value.
$ 13 B A: Prompt subroutine
$13CA: Input and analyze digit
$ 1A D B: Multiply-by-two subroutine
$1415: Division subroutine
$ 1A 7 A: Try a divisor (short)
$147D: Try a divisor (long)
$1465 : Check if remainder zero
$14^2: Log factor if found
$14 A 5 : Check if more to do
$14BCI: Print value subroutine
362 MACHINE LANGUAGE FOR COMMODORE MACHINES

$1<DD: Print factor subroutine


$1504 : Clear output area
$15OF: Convert to decimal and print
$1535: Print a digit with zero suppression
$1515: 30-count values: 1,7,11, etc.

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.

PENTOMINOS INST (instructions)


PENTOMINOS (BASIC, all machines)
PENTOMINOS PET (for PET/CBM)
PENTOMINOS Vb4 (for VIC-20, Commodore 64, and Plus-4)
PENTOMINOS lEfl (for Commodore 128)
PENTOMINOS BlEfl (boot for B128 system)
+ PENTO12Q (program for B128)
+ XFE R (transfer sequence for B128)

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).

The machine language program uses no indirect addressing and no sub


routines. That is not necessarily good practice; it is largely a result of
writing the program logic to exactly match the BASIC program.

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.

As with FACTORS, the machine language program is relocated by BASIC


so that it starts at hexadecimal 15bD (with tables starting at $12Ffi)
regardless of where it was originally loaded. Again, this is necessary for
the VIC-20 and proves to be convenient for study purposes on all ma
chines—except the B-128 version, where this relocation does not happen.
APPENDIX J 363

Students wishing to disassemble PENTOMINOS will find the following


information useful:

VARIABLES (see C128 note):

$D33C—piece number, BASIC variable P


$D33D to $D33E—variables W1 and W2, board size
$D33F—P1, number of pieces placed
$D34D to $U3AB—U(..) log of pieces placed
$D3AC to $0357—T(..) rotation of piece
$D35fl to $D35C—X(..) location of piece
$D35D to $D3bl—Y(..) location of piece
$D3b5 to $D37D—tables to place a piece
$D37F to $D3qC—board "edge" table
$D3qD to $D3Dfl—B(...) the board.

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.

PROGRAM (see C128 note):

$15bD: Start, BASIC line 1070


$15A< : Clear screen, BASIC line 1120
$ 15 A n: Clear variables, set up
$15CC: Find space, BASIC line 2010
$lbDD: Get new piece, BASIC line 2030
SlbDq : Try piece, BASIC line 2060
$lbflb: Put piece in, BASIC line 2120
$lbED: Print "Solution11, BASIC line 2170
$17 Dl: Undraw piece, BASIC line 2190
$17 AB: Rotate piece, BASIC line 2260
$17BC: Give up on piece, BASIC line 2280
$17C1: Look for new piece, BASIC line 2300

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

STRING THING (BASIC, for PET/CBM)


STRING THING Vb4 (BASIC, for VIC-20, Commodore 64, Plus-4)
STRING THING lEfl (Commodore 128)
JSAMPLE FILE

A simple machine language program , POKEable directly from BASIC, to


substitute for an INPUT# statement.

INPUT# has several limitations that sometimes make it awkward for use
with files:

• No more than 80 characters may be read.


• The comma or colon character will break up input.
• Leading spaces will disappear.

STRING THING reads everything up to the next RETURN or end of


file. It is pure BASIC, but POKEs machine language into the cassette
buffer area. It finds the first variable and uses it as an input buffer.

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.

Accumulator: (3) The A register; the register used for arithmetic.


Address bus: (1) A bus that signals which part of memory is wanted
for the next memory operation.
Address mode: (5) The manner in which an instruction reaches in
formation within memory.
Address: (1) The identity of a specific location within memory.
Algorithm: (1) A method or procedure to perform a computing task.
Arithmetic shift or rotate: (4) A shift or rotate that usually preserves
the sign of a number.
Assembler: (2) A program that assembles or changes source code
into object code.
Assembly: (1) The process of changing source code into object code.
Assembly code: (1) Also called source code. A program written in a
somewhat human-readable form. Must be translated ("assembled") before
use.

365
366 MACHINE LANGUAGE FOR COMMODORE MACHINES

Assembly language: (1) The set of instructions, or language, in which


a source program must be written before assembly.
Binary: (1) Something that has two possible states; a number based
on digits, each of which has two possible states.
Bit: (1) A binary digit; the smallest element of information within a
computer.
Bootstrap: (6) A program that starts up another program.
Breakpoint: (8) A location where the program will stop so as to allow
checking for errors.
Bug: (8) An error within a program.
Bus: (1) A collection of wires connecting many devices together.
Byte: (1) Eight bits of information grouped together; the normal mea
sure of computer storage.
Calling point: (2) The program location from which a subroutine is
called into play; the subroutine will return to the calling point when finished.
Channel: (8) A path connecting the computer to one of its external
devices.
Comment: (8) A program element which does not cause the computer
to do anything, used as advice to the human program reader.
Commutative: (3) A mathematical operation that works both ways,
e.g., 3 + 4 gives the same result as 4 + 3.
Control bus: (1) A bus that signals timing and direction of data flow
to the various connected devices.
Data bus: (1) A bus used to transfer data between memory and the
microprocessor.
Debugging: (8) Testing a program to uncover possible errors.
Decimal: (1) A number system based on a system of ten digits; the
"normal" numbering system used by humans.
Decrement: (2) To make smaller by a value of one.
Descriptor: (6) A three-byte set of data giving a string's length and
its location.
Disassembler: (2) A program that changes object code into assembly
code to allow inspection of a program.
Disassemble: (2) To change object code into assembly code. Similar
to a LIST in BASIC.
Dynamic string: (6) A string that must be placed into memory after
being received or calculated.
Effective address: (2) The address used by the processor to handle
data when executing an instruction. It may differ from the instruction ad
dress (or "operand") because of indexing or indirect addressing.
Event flag: (7) A flag that signals that some event has happened.
Execute: (1) To perform an instruction.
GLOSSARY 367

File: (8) A collection of data stored on some external device.


Flag: (3) An on/off indicator that signals some condition.
Floating accumulator: (7) A group of memory iocations used by BASIC
to perform calculations on a number.
Garbage collection: (6) A BASIC process in which active strings are
gathered together and inactive strings are discarded. On some computers
this can be quite time consuming.
Increment: (2) To make larger by a value of one.
Index: (2) To change an address by adding the contents of an index
register.
Index register: (2) The X or Y registers, which may be used for chang
ing effective addresses.
Indirect address: (5) An addressing scheme whereby the instruction
contains the location of the actual address to be used; an address of an
address.
Instruction: (1) An element of a program that tells the processor what
to do.
Interrupt: (1) An event that causes the processor to leave its normal
program so that some other program takes control, usually temporarily.
Interrupt enable register: (7) A location within an IA chip that deter
mines whether or not a selected event will cause an interrupt.
Interrupt flag: (7) A signal within the IA indicating that a certain event
has requested that an interrupt take place.
Interrupt flag register: (7) A location within the IA where interrupt
events can be detected and turned off if desired.
Interrupt source: (7) The particular event that caused an interrupt.
Since many things can do this, it's usually necessary to identify the specific
source of the interrupt.
Kernal: (2) Commodore's operating system.
Label, symbolic address: (8) A name identifying a memory location.
Latch: (7) A flag that "locks in."
Load: (1) To bring information from memory into the processor. A
load operation is a copying activity; the information still remains in memory.
Logical file number: (8) The identity of a file as used by the program
mer.
Logical operator: (3) An operation that affects individual bits within a
byte: AND, ORA, and EOR.
Logical shift or rotate: (4) A shift that does not preserve the sign of
a signed number.
Machine code: (1) Instructions written in machine language.
Machine language: (1) The set of commands that allow you to give
instructions to the processor.
368 MACHINE LANGUAGE FOR COMMODORE MACHINES

Machine language monitor: (1) A program that allows communication


with the computer in a manner convenient for machine language pro
gramming.
Memory: (1) The storage used by a computer; every location is iden
tified by an address.
Memory mapped: (1) Circuits that can be reached by the use of a
memory address, even though they are not used for storage or memory
purposes.

Memory page: (5) A set of 256 locations in memory, all of whose


addresses have the same "high byte."
Microcomputer: (1) A computer system containing a microprocessor,
memory, and input/output circuits. A computer built using microchips.
Microprocessor: (1) The central logic of a microcomputer, containing
logic and arithmetic. A processor built on a microchip.
Monitor: (1) A program that allows the user to communicate with the
computer. Alternatively, a video screen device.
Non-maskable interrupt, NMI: (7) A type of interrupt that cannot be
disabled.
Non-symbolic assembler: (2) An assembler in which actual addresses
must be used.
Object code: (1) The machine language program that will run in the
computer.
Octothorpe: (2) Sometimes called a numbers sign, a pounds sign, a
hash mark. The "#" symbol.
Operand: (1) The part of an instruction following the op code that
usually signals where in memory the operation is to take place.
Operating system: (1) A set of programs with a computer that takes
care of general work such as input/output, timing, and so on.
Operation code, op code: (1) The part of an instruction that says
what to do.
Overflow: (3) Condition caused by an arithmetic operation generating
a result that is too big to fit in the space provided.
Pointer: (6) An address held in memory, usually in two bytes.
Processor status word, status register: (3) A processor register that
holds status flags.
Pull: (7) To take something from the stack.
Push: (7) To put something on the stack.
Random access memory, RAM: (1) The part of a computer's memory
where information can be stored and recalled.
Read: (1) To obtain information from a device.
Read only memory, ROM: (1) The part of a computer's memory where
fixed information has been stored. New information cannot be stored in a
ROM; it is preprogrammed.
GLOSSARY 369

Register: (1) Location within a processor where information can be


held temporarily.
Screen editing: (1) The ability to change the screen of a computer
and cause a corresponding change in memory.
Screen memory: (2) The part of a computer holding the information
displayed on the screen. Changing screen memory will change the screen;
reading screen memory will reveal what is on the screen.
Selected: (1) A chip or device that has been signaled to participate
in a data transfer. If the chip or device has not been selected, it will ignore
data operations.
Self-modifying: (7) A type of program that changes itself as it runs.
Rare, and not always considered good programming practice.
Signed number: (3) A number that holds a value that may be either
positive or negative.
Source code: (1) Instructions written in assembly language; usually,
the first code written by the programmer before performing an assembly.
Stack: (7) A temporary, or "scratch pad," set of memory locations.
Status register, processor status word: (3) Within the processor, a
register that holds status flags.
Store: (1) To transfer information from the processor to memory. The
store operation is a copying activity: the information still remains in the
processor.

Subroutine: (2) A set of instructions that can be called up by another


program.

Symbolic address, label: (7) A name identifying a memory location.


Symbolic assembler: (2) An assembler in which symbolic addresses
may be used. This is more powerful than a non-symbolic assembler.
Testable flag: (3) A flag that can be tested by means of a conditional
branch instruction.
Two's complement: (3) A method of representing negative numbers.
With single byte numbers, -1 would be represented by $FF.
Unsigned number: (3) A number that cannot have a negative value.
Write: (1) To send information to a device.
Zero page: (5) The lowest 256 locations in memory. Locations whose
addresses begin with hexadecimal $00...
■J
Index
A, X, and Y data registers, 9, 11, linking with, 30-31
46, 47, 142 machine language exchanging
Absolute addressing, 148 data, 104-108
Absolute indexed mode, 77-78 memory layout, 92-102
Absolute indirect, 149 variables, 102-105
Absolute mode, 75-76 BCC, Branch on carry clear, 87,
Accumulator addressing, 148 149
Accumulator mode, 74 BCS, Branch on carry set, 149
ADC, Add memory to accumulator BEQ, Branch on result zero, 149
with carry, 149 Binary, defined, 2
Addition, 58-60 Bit, defined, 2
Address, defined, 3 BIT, Test bits in memory with
Address bus, 3-5 accumulator, 142, 149
Addressing modes, 72-89, Bit map mode on the 6566/6567,
148-149 328-330
Algorithms: BMI, Branch on result minus, 149
decimal to hexadecimal, 7 BNE, Branch on result not zero,
hexadecimal to decimal, 7 149
AND, "AND" memory with BOS, Bottom of string, 94-95
accumulator, 121, 149 BPL, Branch on result plus, 149
ASCII, 25, 50, 249-250 Branches and branching, 79-80
ASL, Shift left one bit (memory or Branch instructions, 141
accumulator), 61-62, 149 BRK, Force-break, 72, 115, 116,
Assemblers: 142, 143, 149, 279
nonsymbolic, 27 Bus:
symbolic, 143-144 address, 4-5
control, 5
BASIC: defined, 3
breaking into, 124-125 see also Data bus
infiltrating, 122-124 BVC, Branch on overflow clear, 150

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

adding a command, 125-126, Indexed indirect addressing, 149


256, 271-273 Indexed indirect mode, 83-84
addition, 263-264 Indexed zero page addressing, 148
for Commodore C128, 257-276 Indexing modes:
file transfer, 274-276 absolute, 77-78
input, 263 indirect, 81-82
interrupt, 119-120, 254-255, zero page, 78
270-271 Index registers, 33
loops, 262-263 Indirect indexed addressing, 149
output, 273-274 Indirect indexed mode, 81-82
print, 26-27, 259-262 Infiltrating BASIC, 122-124
screen manipulation, 265-266 Input, 50-52, 133
Extended color mode of the 6566/ Input:
6567, 328 GETIN, 50-51
switching, 136-137
File transfer program, 138-141 INS, increment, 72
Flags, 40-46 Instruction execution, 10-11
Floating point variables, 103 Instruction set, 141-142, 147
Free memory, 94-95 alphabetic sequence, 149-151
Integer variables, 104
GETIN, Gets an ASCII character, Interface adaptor chips, 9, 50,
25, 133 120-122, 142
Glossary, 365-369 Interrupt enable register, 122
Interrupt flag register, 121
Handshaking, 318-319 Interrupt processing, 40
Hexadecimal notation, 5-6 Interrupt request, 115
Hexadecimal notation to decimal, INX, Increment index X by one, 150
6-7 INY, Increment index Y by one, 150
IRQ, Interrupt request, 115,
IA, Interface adaptor chips, 9, 50, 117-118
120-122, 142
IER, Interrupt enable register, 122 JMP, Jump to new location,
IFR, Interrupt flag register, 121 80-81, 141-142, 150
Immediate addressing, 148 JSR, Jump to new location saving
Immediate mode, 74-75 return address, 114-115, 150
Implied addressing, 148 Jumps in indirect mode, 80-81
Implied mode, 72-74 Jump subroutine, 142
INC, Increment memory by one, 74,
150 Kernal, 24
Increment and decrement Kernal subroutines:
instructions, 141 CHKIN, 136
Indexed absolute addressing, 148 CHROUT, 25, 133, 134
374 MACHINE LANGUAGE FOR COMMODORE MACHINES

CLRCHN, 136 Upgrade and BASIC 4.0 systems,


GETIN, 51 172-179
STOP, 52 VIC 20, 181-187
VIC 6522 usage, 189-190
VIC 6560 chip, 188
LDA, Load accumulator with
Microprocessor chips, 650X, 3-4
memory, 150
MLM, Machine language monitors,
LDX, Load index X with memory,
14, 284
150
MLM commands, 16-17, 99-100
LDY, Load index Y with memory,
.G command, 17
150
.M command, 16
Light pen, 335
.R command, 16
LOAD, 100-101
Save command, 99-100
Logical and arithmetic routines, 141
.X command, 16
Logical operators, 47-50
Modes:
Loops, 32-34, 262-263
absolute indexed, 77-78
LSR, Shift one bit right (memory or
addressing, 72-89
accumulator), 63-64, 150
all of zero page, 78
indexed, indirect, 83-84
Machine language and BASIC indirect, indexed, 81-82
exchanging data, 104-108 jumps in indirect, 80-81
Machine language linking with no address, 72-74
BASIC, 30-31 no address accumulator, 74
Machine language monitor SAVE, not quite an address, 74-75
99-100 relative address, 79-80
Memory, free, 94-95 single address, 75-76
Memory contents: zero page, 76-78
changing, 17 Monitors:
displaying, 17 basic, 14
Memory elements, 8-9 display, 15
Memory interface of the 6566/6567, extensions, 27-29
337-340 machine language (MLM),
Memory layout, BASIC, 92-102 14-15, 290-291
Memory maps: machine language SAVE,
B series, 203-212 99-100
CBM8032, 179-180 Multi-color character mode of the
Commodore PLUS/4 "TED" chip, 6566/6567, 327
201-203 Multiplication, 62-63
Commmodore 64, 191-200 by two, 61-62
Commodore 128, 213-230
FAT-40 6545 CRT, controller, N flag, 42-43, 45, 46
179-180 Non-maskable interrupt (NMI), 115,
"Original ROM" PET, 168-172 118
INDEX 375

NOP, No operation, 72-74, 85, interrupt, 119-120, 254-255


150 print, 26-27
NOP BRK, No operation, break, Programs, file transfer, 138-141
false interrupt, 142 Pull information, 142
Numbers: Push information, 142
comparing, 61-62 Push processor status, 114
signed, 43-44, 58
unsigned, 58
RAM, Random access memory, 8
Numeric variables, 104
Register map of the 6566/6567,
338-339
OPEN, 133-134
Registers, 9-10, 18
ORA, "OR" memory with
A, X, and Y, 9, 11,46,47
accumulator, 47, 48, 49, 121,
index, 33
150
status, 45-46
Output, 133
Relative addressing, 148-149
controlling, 24-36
mode, 79-80
examples of, 135-136
ROL, Rotate one bit left (memory or
switching, 133-135
accumulator), 62, 150
Overflow, 44
ROM, Read only memory, 8-9
link, 80-81
PC, Program control register, 9
ROR, Rotate one bit right (memory
PEEK, 5, 104
or accumulator), 63-64, 150
PHA, Push accumulator on stack,
Rotate, comments, 64-65
113, 150
RTI, Return from interrupt, 115, 150
PHP, Push processor status on
RTS, Return from subroutine, 65,
stack, 114, 150
114-115, 151
PIA, Peripheral interface adaptor
RUN STOP key, 51-52
6520, 120, 294-298
PLA, Pull accumulator from stack,
113, 114, 150 6502 Instruction set, 147
PLP, Pull processor status from 6509 Instruction set, 147
stack, 114, 150 6510 Instruction set, 147
Pointers, fixing, 102 6520 (PIA) Peripheral interface
POKE, 5, 26, 104 adaptor, 294-298
Program: 6522 (VIA) Versatile interface
entering a, 18-19 adaptor, 309-318
running a, 30 6525 Tri-port interface, 352-356
Program Counter, 9-11, 149 6526 (CIA) Complex interface
Programming model, 151 adaptor, 318-325
Programming projects, 11-13, 6545-1 (CRTC) CRT controller,
52-54, 84-88, 252-278 299-304
adding a command, 125-126, 6560 (VIC) Video interface chip,
256 304-309
376 MACHINE LANGUAGE FOR COMMODORE MACHINES

6566/6567 (VIC II) chip KERNAL, 24


specifications, 325-340 prewritten, 24-25
6581 (SID) Sound interface device, STOP, 25, 51-52
chip specifications, 340-351 Subtraction, 60-61
7501 Insruction set, 147 Supermon program, 27, 284-289
SAVE, 34, 141 Supermon+ program, 290-291
stopgap, 34-35 Symbolic assemblers, 143-144
SBC, Subtract memory from SYS, Go to the address supplied,
accumulator with borrow, 151 116
Screen codes, 242-250
Screen manipulations, 84-88 TAX, Transfer accumulator to index
Screen memory address, 21 X, 113, 151
SEC, Set carry flag, 151 TAY, Transfer accumulator to index
SED, Set decimal mode, 151 Y, 72, 113, 151
SEI, Set interrupt disabler status, Testable flags, 40-45
118, 151 Time of day clock, 321
Shift, comments on, 64-65 Timing, machine language program,
Shift and rotate instructions, 132-133
61-63, 74, 141 TOM, Top of memory, 93-94
Signed numbers, 43-44, 58 Tri-port interface 6525, 352-356
Single address mode, 75-76 TSX, Transfer stack pointer to
SOA, Start of arrays, 93 index X, 151
SOB, Start of BASIC, 92 Two's complement, 43
Sound interface device (SID) chip TXA, Transfer index X to
specification 6591, 340-351 accumulator, 151
SOV, Start of variables, 93, TXS, Transfer index X to stack
97-102 register, 151
SP, Stack pointer register, 9 TYA, Transfer index Y to
SR, Status register, 9 accumulator, 151
STA, Store accumulator in memory,
151 Uncrashing techniques, 280-281
Stack, 112-115 Unsigned numbers, 58
Status register, 45-46 USR, Go to a fixed address and
Stop, 25, 51-52 execute machine code there as
Stopgap save command, 34-35 a subroutine, 116-117
Storage, temporary, 112-115
String variables, 103 Variables, 102-105
STX, Store index X in memory, 151 V flag, 44, 45, 46
STY, Store index Y in memory, 151 VIA, Versatile interface adaptor,
Subroutines: 120-121
CHROUT, 25 VIC II chip specifications 6566/
GETIN, 25, 50-51 6567, 325-340
INDEX 377

(VIC) Video interface chip 6560, Zero page addressing, 148


304-309 Zero page mode, 76-78
indexed, 78
Wedge, 122-124 Zflag, 40-41,45, 46
program, 124-125
■J
■J
Pull up a chair and sit down with Jim Butterfield—the Commodore guru
himself—and get the answers to all your questions about machine lan
guage with . . .

Machine Language for the


Commodore 64, 128, and Other
Commodore Computers
Revised & Expanded Edition /by Jim Butterfield

Authored by the world-renowned expert on all facets of the Commodore,


this comprehensive tutorial introduces programmers of all levels to the
principles of machine language—what it is, how it works, and how to
program with it. With speed and versatility, you'll share in Butterfield's
vast experience as you:

• Learn-by-doing—with easy-to-follow, step-by-step instructions, exam


ples, and exercises
• Uncover the critical elements to understanding machine language,
machine architecture, and machine language tools
• Master principles of good coding practices—for more efficient and
effective programming
• Explore the inner workings of the Commodore, write and enter a simple
program, learn the details behind output, flags, logic, input, subrou
tines, address modes, and more!

Not only an excellent tutorial, it's an invaluable resource you'll refer to


again and again!

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

Cover illustration by David Joly


Cover Design by Ben Santora

A Brady Book ■ Published by Prentice Hall Press • New York

0 21898 66417 ISBN O-

You might also like