Using 6502 Assembly Language by Randy Hyde
Using 6502 Assembly Language by Randy Hyde
USING 6502
ASSEMBLY LANGUAGE
With an Introduction to Sweet-16
DATAMOST
by Randy Hyde
Apple II is a trademark of Apple Computer, Inc.
DATAMOST
8943 Fullbright Ave., Chatsworth, CA 91311 (213) 709-1201
cover
*****************************************************************
USING 6502
ASSEMBLY
LANGUAGE
*****************************************************************
By Randy Hyde
A Product of
DATAMOST, INC.
8943 Fullbright Avenue
Chatsworth, CA 91311
(213) 709-1202
*****************************************************************
-ACKNOWLEDGMENTS-
The word APPLE and the Apple logo are registered trademarks
of APPLE COMPUTER, INC.
*****************************************************************
TABLE OF CONTENTS
NOTE
An alphabetical index is located in the back of this manual.
Chapter 1
INTRODUCTION 1-1
Purpose of Manual 1-1
Scope of Manual 1-1
General 1-1
Chapter 2
SYMBOLISM 2-1
General 2-1
Bit Strings 2-3
Binary Arithmetic 2-8
Unsigned Integers 2-9
Nibbles (NYBBLES?), Bytes, and Words 2-10
Signed Integers 2-11
Hexadecimal Numbers 2-13
Radix and Other Nasty Diseases 2-14
ASCII Character Set 2-14
Using Bit Strings to Represent Instructions 2-16
Chapter 3
REGISTERS, INSTRUCTION FORMATS,
AND ADDRESSING 3-1
General 3-1
Accumulator (A or ACC) 3-3
X-Register (X) 3-3
Y-Register (Y) 3-3
Stack Pointer (SP) 3-4
Program Status Word (P or PWS) 3-4
Program Counter (PC) 3-4
Instruction Format (6502) 3-4
Two and 3-Byte Instructions 3-6
6502 Addressing Modes 3-8
iii
*****************************************************************
Chapter 4
SOME SIMPLE INSTRUCTIONS 4-1
General 4-1
Assembly Language Source Format 4-1
Introduction to Real Instructions 4-4
Register Increments and Decrements 4-8
Labels and Variables 4-9
Expressions in the Operand Field 4-11
Chapter 5
ASSEMBLY LANGUAGE 5-1
General 5-1
Example Program 5-2
JMP Instruction 5-3
Processor Status (P) Register 5-5
Break Flag (B) 5-6
Decimal Flag (D) 5-6
Interrupt Disable Flag (Z) 5-6
Condition Code Flags (N, V, Z, C) 5-7
Branch Instructions (6502) 5-9
Loops 5-10
Comparisons 5-11
IF/THEN Statement Simulation 5-14
FOR/NEXT Loop Revisited 5-14
Testing Boolean Values 5-18
Chapter 6
ARITHMETIC OPERATIONS 6-1
General 6-1
Unsigned Integer (Binary) Arithmetic 6-1
Subtraction 6-4
Signed Arithmetic 6-5
Signed Comparisons 6-7
Binary Coded Decimal Arithmetic 6-8
Unsigned BCD Arithmetic 6-8
Signed BCD Arithmetic 6-10
Arithmetic Review 6-10
iv
*****************************************************************
Chapter 7
SUBROUTINES AND STACK PROCESSING 7-1
General 7-1
Variable Problems 7-4
Passing Parameters 7-13
Chapter 8
ARRAYS, ZERO PAGE, INDEXED, AND
INDIRECT ADDRESSING 8-1
General 8-1
Zero Page Addressing 8-1
Arrays in Assembly Language 8-3
Initializing Arrays at Assembly Time 8-8
Using Index Registers to Access Array Elements 8-10
Indirect Addressing Mode 8-13
Indirect Indexed Addressing 8-16
Indexed Indirect Addressing Mode 8-18
Chapter 9
LOGICAL, MASKING, AND BIT OPERATIONS 9-1
General 9-1
Complement Function 9-2
AND Function 9-2
OR Function 9-3
EXCLUSIVE-OR Function 9-4
Bit String Operations 9-4
Instructions for Logical Operations 9-5
Masking Operations 9-7
Shift and Rotate Instructions 9-13
Shifting and Rotating Memory Locations 9-16
Using ASL to Perform Multiplication 9-17
Using Shifts to Unpack Data 9-19
Using Shifts and Rotates to Pack Data 9-20
Chapter 10
MULTIPLE-PRECISION OPERATIONS 10-1
General 10-1
Multiple-Precision Logical Operations 10-1
*****************************************************************
Chapter 11
BASIC I/O 11-1
General 11-1
Character Output 11-1
Standard Output and Peripheral Devices 11-9
Character Input 11-11
Inputting a Line of Characters 11-13
Chapter 12
NUMERIC I/O 12-1
General 12-1
Hexadecimal Output 12-1
Outputting Byte Data as a Decimal Value 12-2
Outputting 16-Bit Unsigned Integers 12-4
Outputting Signed 16-Bit Integers 12-6
An Easy Method of Outputting Integers 12-6
Numeric Input 12-8
Unsigned Decimal Input 12-11
Signed Decimal Input 12-17
Chapter 13
MULTIPLICATION AND DIVISION 13-1
General 13-1
Multiplication 13-1
Division Algorithms 13-7
vi
*****************************************************************
Chapter 14
STRING HANDLING OPERATIONS 14-1
String Handling 14-1
Declaring Literal Strings 14-5
String Assignments 14-5
String Functions 14-7
String Concatenation 14-9
Substring Operations 14-11
String Comparisons 14-12
Handling Arrays of Characters 14-17
Chapter 15
SPECIALIZED I/O 15-1
Apple I/O Structure 15-1
Chapter 16
AN INTRODUCTION TO SWEET-16 16-1
Sweet-16 16-2
Sweet-16 Hardware Requirements 16-10
Chapter 17
DEBUGGING 6502 MACHINE LANGUAGE
PROGRAMS 17-1
General 17-1
GO Command (G) 17-2
Initializing Registers and Memory 17-3
Modifying Instruction Code (Patching) 17-6
Program Debugging Session 17-10
Appendix A
APPLE II COMPUTER TABLES,
CHARTS, AND GRAPHS A-1
vii
*****************************************************************
CHAPTER 1
INTRODUCTION
PURPOSE OF MANUAL.
SCOPE OF MANUAL.
GENERAL.
1-1
*****************************************************************
prior 6502 experience, the first few chapters may contain infor-
mation which you have seen previously. AVOID THE TEMPTA-
TION TO SKIP ANY MATERIAL! If one important detail is not
understood, the remainder of the book may prove impossible to
understand. So take the time to review all of the available material
and make sure that you understand the reviewed section before
going on. Obviously, if you are a beginner it is very important that
you understand each section before continuing.
PROGRAMMING A MICROCOMPUTER
by Caxton C. Foster
1-2
*****************************************************************
ZERO: The first 256 bytes in the memory space (page number
PAGE 0) of the APPLE II computer are often referred to
as the "zero page" or "page zero." Naturally
there is a "page one," a "page two," etc., but
the use of the first 256 bytes in the machine
occurs so often that the term, "zero page,"
has come into common use.
1-3
*****************************************************************
1-4
*****************************************************************
The fact that you have read the text this far shows that you
have an interest in the subject. Nevertheless, some of you are
certain to have some misconceptions about the language.
Assembly language should be used when speed is the foremost
requirement in a program, or possibly when you need to control
a peripheral device, or maybe you have a specialized application
that cannot be executed easily (or cleanly) in one of the high-
level languages on the APPLE II computer.
DATAMOST, INC.
8943 Fullbright Avenue
Chatsworth, CA. 91311
(213) 709-1202
1-5
*****************************************************************
CHAPTER 2
SYMBOLISM
GENERAL.
When you see the number 4, what do you think? The number
4 is simply a symbol connected with the concept of four items.
When humans communicate, they use several symbols to relay
their ideas. As such, humans are very adaptive. If I told you that
from now on we'll use the symbol "- -" to represent four, you
could make the change. It might not be easy, but the change is
possible.
Computers, on the other hand, are very stupid. They are not
adaptive and understand only a very low-level language which
humans have considerable trouble understanding. This language
is not "assembly" or "machine" language. Assembly, or machine
language, is actually a human convention that makes an even
lower-level language acceptable! The actual low-level language
understood by a computer consists of different voltage levels on
different wires within the machine. Although, with lots of educa-
tion, humans can understand what each of these voltage levels
mean (and in fact your friendly neighborhood computer repair
man should), it certainly isn't very convenient. As such, we usually
2-1
*****************************************************************
rename the voltage levels something else (bits, true, false, 0, 1,
etc.). We do the same thing in spoken languages all the time. For
instance, "deux" (French) usually gets translated to "two"
(English). Renaming voltage levels "bits" and groups of bits
"words" performs this same function. We're merely taking one
symbol, which is hard to understand, and translating this symbol
to one easier to understand.
2-2
*****************************************************************
false), signs (+ or -), yes or no, on or off, and any other user-
defined binary quantities (husband/wife, boy/girl, ... you get the
idea).
Now that we have a bit to play around with, would you like
to play around a bit? Let's define some operations on this bit.
First, we need to define an ordinality for our binary values. This
is necessary because often we need to compare one value to
another to determine which is the greater. "0" and "1" are easy,
one is always greater than zero. For the other binary values we
need to use our intuition to decide on the ordinality. "True" should
be greater than "false," so let's assign true the value "1" (or +5v)
and false the value "0" (or 0v). Yes/no, on/off, etc., should be
assigned in a similar manner. When it comes to data types, such
as male/female, the choice is arbitrary. If you're a male you'll
probably pick the "male" data type as being larger; if you're a
female you'll probably pick "female" as being the greater value.
BIT STRINGS.
2-3
*****************************************************************
0 1 0 0 0 0 0 0 0 0 0
1 0 1 0 0 0 0 0 0 0 0
2 0 0 1 0 0 0 0 0 0 0
3 0 0 0 1 0 0 0 0 0 0
4 0 0 0 0 1 0 0 0 0 0
5 0 0 0 0 0 1 0 0 0 0
6 0 0 0 0 0 0 1 0 0 0
7 0 0 0 0 0 0 0 1 0 0
8 0 0 0 0 0 0 0 0 1 0
9 0 0 0 0 0 0 0 0 0 1
*****************************************************************
Since we now have two additional values, why not use them
to represent the values two and three? If we do this, we wind up
with the following:
2-5
*****************************************************************
2-6
*****************************************************************
7 6 5 4 3 2 1 0
7 6 5 4 3 2 1 0
2 2 2 2 2 2 2 2
The first bit represents 2^0 (any number raised to the power
"0" is one), the second bit represents two raised to the first power
(i.e, 2^1), the third bit represents two raised to the second power
(2^2), etc. For example, binary 1100101 represents 1x2^6 + 1x2^5
+0x2^4 + 0x2^3 + 1x2^2 + 0x2^1 + 1x2^0 or 101 in decimal.
*****************************************************************
BINARY ARITHMETIC.
95
+67 add 5 to 7
---
2 result is 2, carry is 1.
95
+67 add 9 to 6 plus one (from the carry).
---
62 result is 6, carry is 1.
After the carry is added in, we get the final result of 162.
Binary addition works the same way, but is even easier. It's
based on seven rules:
1) 0 + 0 = 0; carry = 0
2) 1 + 0 = 1; carry = 0
3) 0 + 1 = 1; carry = 0
4) 1 + 1 = 0; carry = 1
5) 0 + 0 + carry = 1; carry = 0
6) 1 + 0 + carry = 0; carry = 1
7) 1 + 1 + carry = 1; carry = 1
0110
0111
----
1 C = 0
2-8
*****************************************************************
0110
0111
----
01 C = 1
0110
0111
----
101 C = 1
0110
0111
----
1101 C = 0
01101100 1101101
11101011 1111011
-------- -------
101010111 11101000
UNSIGNED INTEGERS.
2-9
*****************************************************************
Note that there are two nibbles in a byte and two bytes in a
word. This generates some additional terminology. Each bit string
has a low-order bit and a high-order bit. The low-order bit is
always bit number 0, and the high-order bit is equal to (n - 1)
where n is the number of bits in the bit string. For a nibble, n is
four so the high-order bit is bit number three (remember, we start
with zero!). For a byte (n = 8) the high-order bit is bit number 7
and for a word (n = 16) the high-order bit is bit number 15.
EXAMPLES:
Bit # 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
---------------------------------------------
NIB
1 0 1 0
BYTE
0 0 1 1 0 0 1 1
WORD
0 0 1 0 1 1 1 0 1 1 1 0 0 0 1 1
2-10
*****************************************************************
SIGNED INTEGERS.
0000000000000010
2-11
*****************************************************************
1111111111111101
1111111111111110
To take the two's complement of minus two, we first invert all the
bits:
0000000000000001
0000000000000010
Why even bother with such a weird format? After all, it's
probably much simpler to just use the high-order standard binary
format. Well, a simple addition problem may help clear things up.
Consider the addition of two plus minus two.
0000000000000010
1111111111111110
----------------
0000000000000000 carry = 1
2-12
*****************************************************************
HEXADECIMAL NUMBERS.
Binary numbers are fine for examples. But when used for
conveying information to people, they tend to be too bulky. Can
you imagine having to write out one hundred 16-bit numbers in
binary? Or having to read them? Several years ago programmers
began using the octal (base eight) numbering system to compact
the large binary numbers. With the octal system it is possible to
cram 16 bits of information into six digits. The octal numbering
system is still popular on several minicomputers today. When
microcomputers came along, manufacturers switched to the hex-
adecimal numbering system which made it possible to get 16 bits
of information into only four digits! The only drawback to the hex-
adecimal numbering system is that most people are not familiar
with it. The hexadecimal system (base 16) contains 16 distinct
digits. The first ten digits are the familar numeric characters 0 thru
9 and the last six digits are the alphabetic characters A thru F.
Hexadecimal numbers have the values:
BINARY DECIMAL IEXADECIMAL
0000 0 0
0001 1 1
0010 2 2
0011 3 3
0100 4 4
0101 5 5
0110 6 6
0111 7 7
1000 8 8
1001 9 9
1010 10 A
1011 11 B
1100 12 C
1101 13 D
1110 14 E
1111 15 F
Why all the fuss over hexadecimal numbers (or hex numbers
as they are usually referred to)? They are easy to convert to
binary and vice versa. Decimal numbers, unfortunately, are not
as easy to use. For example, 11111100 is not easily converted
to 252 decimal, but it is a trival matter to convert it to the hex-
adecimal number FC. Clear as mud, right? It's actually quite sim-
ple once you learn one little trick. In order to convert a binary
number to a hexadecimal number you must first adjust the binary
number so that it contains the number of bits which are a multiple
2-13
*****************************************************************
2-14
*****************************************************************
2-15
*****************************************************************
Until now we have assumed that bit strings are used only to
represent data of some type. This is not always the case. A bit
string can also be used to represent a command.
2-16
*****************************************************************
2-17
*****************************************************************
CHAPTER 3
REGISTERS, INSTRUCTION
FORMATS, AND
ADDRESSING
GENERAL.
Up until now, our discussion of data types has been, for the
most part, unrestricted. Unfortunately, in the "real" world of com-
puters several restrictions apply which limit the size and feasibility
of the operation we wish to perform. In order to be able to write
good programs the user must first learn the limitations, and
advantages, of the APPLE II computer.
*****************************************************************
3-2
*****************************************************************
1) Accumulator (A or ACC)
2) X-register (X)
3) Y-register (Y)
4) Stack Pointer (SP)
5) Program Status Word (P or PSW)
6) Program Counter (PC)
ACCUMULATOR (A or ACC).
X-REGISTER (X).
Y-REGISTER (Y).
3-3
*****************************************************************
NOTE
Since locations $100 thru $1FF are reserved for the Stack Pointer
register, NEVER use these locations for data or program storage.
3-4
*****************************************************************
EXAMPLE:
MEMORY
1st INSTRUCTION <- PROGRAM COUNTER
2nd INSTRUCTION
3rd INSTRUCTION
4th INSTRUCTION
5th INSTRUCTION
3-5
*****************************************************************
3-6
*****************************************************************
In addition to the 2-byte instructions, there are also 3-byte
instructions. One good example is the "store the accumulator in
an absolute memory location" instruction. This instruction (which
consists of $8D followed by a 16-bit address) will store the con-
tents of the accumulator at any of the 65,536 different memory
locations available in the 6502 memory space. For example,
($8D, $00, $10) will store the accumulator at location $1000, and
($8D, $C3, $48) will store the accumulator at location $48C3.
EXAMPLE:
-- ETC.
--
--
WARNING
3-7
*****************************************************************
mulator with $FF and then proceeded to destroy the first instruc-
tion ($A9 stored at location $1234) by storing a $FF over the top
of the $A9, leaving you with the following code:
LOC DATA/CODE
1234 FF
1235 FF
1236 8D
1237 34
1238 12
1239 --
ETC. ETC.
With this in mind, be very careful where you store data since
you can easily wipe out your program if you are not careful.
6502 ADDRESSING MODES.
3-8
*****************************************************************
examples, $A9 says, "Load the accumulator with the value con-
tained in the following byte." This is an example of the immediate
addressing mode. The instruction could be worded as, "Load the
accumulator with the byte immediately following the instruction
byte." With this wording the term "immediate addressing mode"
makes a little more sense. Instructions using the immediate
addressing mode are always two bytes long: one byte for the
instruction and one byte for the immediate data.
3-9
*****************************************************************
Since eight bits only allow 256 different values you are limited to
256 different addresses. In the 6502 address space this corre-
sponds to the first 256 locations in the machine (location $00 to
location $FF, also known as 'page zero'). Since the zero page
addressing mode strongly restricts its usage (you're only allowed
to access 1/256th the amount of data possible with the absolute
addressing mode), why should you even bother using it? The first
part of the answer should be obvious. The absolute addressing
mode results in 3-byte instructions whereas the zero page
addressing mode uses only two bytes. You save memory by using
the zero page addressing mode. The second, and less obvious,
reason is that instructions using the zero page addressing mode
execute faster than instructions using the absolute addressing
mode. Page zero is often used for variable storage, and the other
memory locations are often used for program, array, and string
storage.
NOTE
The Y-register can be used in an identical manner. Naturally, the
instruction code is changed, but the effect is the same.
3-10
*****************************************************************
INDIRECT ADDRESSING.
INDIRECT INDEXED BY Y
INDEXED BY X, INDIRECT.
3-11
*****************************************************************
NOTE
3-12
*****************************************************************
CHAPTER 4
SOME SIMPLE
INSTRUCTIONS
NEW INSTRUCTIONS:
GENERAL.
4-1
*****************************************************************
humans understand are often called, the text file, or source code.
Unlike BASIC, which has few restrictions concerning the
arrangement of statements on a line, assembly language has a
very rigid format. An assembly language source statement is
divided into four sections known as "fields." There is a label field,
a mnemonic field, an operand field, and a comment field. Fields
in an assembler are usually separated by at least one blank; often
two or three of these fields are optional.
SOURCE FORMAT:
LABEL
LOO1
A
MONKEY
4-2
*****************************************************************
MNEMONIC FIELD.
OPERAND FIELD.
4-3
*****************************************************************
thing but a comment (see the next section) will produce an error.
The syntax for the indirect, indirect indexed by Y, and indexed
by X indirect will be considered later.
COMMENT FIELD.
LOAD GROUP.
Examples:
4-4
*****************************************************************
Examples:
STORE INSTRUCTIONS.
Now that we can move data into the accumulator, let's dis-
cuss how to store data from the accumulator, X- or Y-register into
external memory. The 6502 store instructions provide us with this
4-5
*****************************************************************
Examples:
4-6
*****************************************************************
LDA $1000
STA $2000
LDA $1001
STA $2001
Transfer X to Y Transfer Y to X
TXA TYA
TAY TAX
- OR - - OR -
4-7
*****************************************************************
4-8
*****************************************************************
Like the transfer instructions, INX, INY, DEX, and DEY are
implied addressing mode instructions and require no operands.
EXAMPLES:
4-9
*****************************************************************
EXAMPLES OF LABELS:
4-10
*****************************************************************
EXAMPLE:
Unlike EQU the value does not specify where the data is to
be stored, but rather how many bytes you wish to reserve for your
variable. Usually this value will be one or two.
4-11
*****************************************************************
these locations using our label scheme? LISA allows simple arith-
metics to be used in the operand field. The operators "+" and
"-" are allowed.
EXAMPLE:
*****************************************************************
CHAPTER 5
ASSEMBLY LANGUAGE
NEW INSTRUCTIONS:
GENERAL.
5-1
*****************************************************************
EXAMPLE PROGRAM.
Let's try writing a program using loads and the BRK instruc-
tion. First, access LISA (see its accompanying documentation for
details) and proceed as follows:
The execution of step 11 informs LISA that the end of the program
has been reached.
5-2
*****************************************************************
If all has gone well the display will appear as indicated below:
!INS
1 LDA #$0
2 LDX #$1
3 LDY #$2
4 BRK
5 END
6
The next five entries on the line are the values contained in
the accumulator, X-register, Y-register, PSW, and stack pointer
when the BRK occurred. As mentioned previously, we will use
the fact that the BRK instruction prints these registers to perform
simple I/O. In essence, the BRK instruction is very similar to the
END and STOP instructions in BASIC.
JMP INSTRUCTION.
5-3
*****************************************************************
J to zero (obviously, after the first time through, location I will also
contain zero).
EXAMPLE:
(Note that the EQU and END statements do not take up a program
location.)
5-4
*****************************************************************
I EQU $0
J EQU $1
LABEL LDA J
STA I
LDA #$0
STA J
JMP LABEL
END
You will note that this is even easier to use than the GOTO
in BASIC because you don't have to worry about using sequential
line numbers, especially when you are branching forward. The
assembler detects a label on the current line by checking column
one. If column one contains an uppercase alphabetic character,
the following characters (up to a space or ":") are assumed to be
part of the label. You must separate the label and the mnemonic
field by at least one space. Also (as mentioned in a previous
chapter), if a label does not appear on the current line, there must
be a blank in column one. If you do not place a blank in column
one, the assembler will treat the mnemonic as a label and attempt
to use the operand (if any) as your mnemonic. The result? An
illegal mnemonic error most likely, so always remember to place
a space in column one if a label does not appear on the current
line of text. One final remark: since LISA detects a label by check-
ing column one of the current source line, labels such as LDA,
LDX, INC, or any other 6502 mnemonic are perfectly valid. For
clarity's sake however, you should avoid mnemonic names as
statement labels.
5-5
*****************************************************************
Four of these bits are set by the result of the previous instruc-
tion. For instance, there is a zero flag in the P-register which is
set if the last result was a zero and reset otherwise. Two of the
remaining flags are explicitly set or cleared by 6502 instructions,
and one of the flags is set if the last interrupt serviced was due
to the execution of the BRK instruction.
The decimal flag (bit number three in the PSW) is set only
by the SED (set decimal) instruction. It can be cleared by the CLD
(clear decimal flag) instruction. The decimal flag is used to deter-
mine what type of arithmetic will be used by the 6502 micropro-
cessor. More information will be given on the decimal flag in the
next chapter.
5-6
*****************************************************************
The condition code flags are the flags affected by the normal
operation of the 6502 microprocessor. The Z (or zero) flag is set
when the last operation executed produced a zero result. For
instance, loading the accumulator with zero sets the zero flag;
decrementing a register when that register previously contained
one gives a zero result and, as such, sets the zero flag; incre-
menting $FF results in a wrap-around to zero giving a zero result.
There are no explicit instructions for setting or clearing the zero
flag. If you want to set it, simply load a register with zero. If you
want to clear it, simply load a register with a value other than
zero. Sneaky trick: If you can't afford to bother the contents of
any of the 6502 registers, simply increment a memory location
known to contain $FF. Location $FFC3 in the Apple monitor is
such a location (both for the old monitor and the new Auto-start
ROM). If you issue the instruction "INC $FFC3," the zero flag will
be set. Likewise, to reset the zero flag without affecting any of the
6502 registers, simply increment a location which does not con-
tain $FF. Location $F800 is a good choice. The Z flag resides in
bit number one of the PSW.
The 6502 N flag is set if the last result was a negative value.
Wait a second! All along we've been saying that there are no
negative values in the 6502 registers. Well, if you remember the
section on two's complement, we used the high-order bit as a sign
flag. If it was set, the number was negative. If it was reset, the
number was positive. We will discuss signed arithmetic later. Here
it is useful to note that the negative (N) flag will contain whatever
was in bit number seven of the previous result. This is sometimes
useful in itself, just to be able to check the status of one of the
bits in a memory location. The N flag resides in bit number seven
of the PSW.
As with the zero flag, there are no explicit set or clear instruc-
tions associated with the N flag. To set the N flag simply increment
any location which contains a value in the range $7F to $FE. The
result of such an increment will always be negative i.e., bit number
seven of the result will always be one. Location $F804 in the
Apple monitor is a good choice. To reset the negative flag simply
increment a memory location which contains a value in the range
$00 to $7E, or $FF. The result of such an increment is always
5-7
*****************************************************************
positive i.e., bit seven of the result will always be zero (memory
location $F800 in the Apple monitor is a good choice).
PGM1:
LDA #$0
BRK
END
PGM2:
LDA #$1
BRK
END
PGM3:
CLC
BRK
END
PGM4:
SEC
BRK
END
PGM5:
LDA #$80
BRK
END
PGM6:
LDA #$7F
BRK
END
5-8
*****************************************************************
Now you know that certain operations affect the flags in the
processor status register. Big deal! How does this help us simu-
late the "IF/THEN" Statement in BASIC? By themselves the status
flags are not very useful in this capacity. Fortunately, the 6502
allows us to test each of the condition code flags with some
branch instructions.
Just as with the JMP instruction you must specify an address (or
label) in the operand field.
EXAMPLE:
LDA #$0
BEQ LBL1
LBL2 LDA #$FF
LBL1 BEQ LBL2
5-9
*****************************************************************
tion LBL1) tests the zero flag. Since the last result obtained was
$FF (from the LDA instruction), this branch will not be taken and
the program will fall through to the next instruction after LBL1.
LOOPS.
5-10
*****************************************************************
COMPARISONS.
5-11
*****************************************************************
5-12
*****************************************************************
LDA I
CMP J
BLT LBL
BEQ LBL
LDA I
CMP J
BEQ EQL
BGE LBL
EQL ---
ETC.
5-13
*****************************************************************
LBL will be taken; if I is less than J, the program will simply fall
through to the instruction at location EQL.
LDA X
CMP #$7
BLT LBL
LDA #$0
STA Y
LBL ---
ETC.
5-14
*****************************************************************
LDA #$1
STA I
LOOP LDA I
CMP #!10
BEQ LBL1
BGE LOPX
LBL1:
;NOTE: The normal code within your loop body goes here.
INC I
JMP LOOP
LOPX BRK
ETC.
LDA #$1
STA I
LOOP LDA I
CMP #$B ;$B = 11 DECIMAL
BGE LOOPX
INC I
JMP LOOP
LOOPX BRK
ETC.
5-15
*****************************************************************
Simply substitute:
BNE *+$5
JMP LBL
If the last operation set the zero flag, the program will drop
through to the JMP instruction and then jump to location LBL. If
the zero flag was not set after the last operation, a branch will
occur to the next instruction after the JMP instruction. This effec-
tively simulates a "LONG BRANCH IF EQUAL" to location LBL.
5-16
*****************************************************************
IF THIS BRANCH
IS OUT OF RANGE USE THIS
--------------- --------
5-17
*****************************************************************
FALSE EQU $0
TRUE EQU $1
5-18
*****************************************************************
at the beginning of your program. True and false will not be used
as memory locations, but rather as symbolic constants. Now your
programs will read:
LDA #FALSE
STA I
LDA #TRUE
STA FLAG
instead of:
LDA #$0
STA I
LDA #$1
STA FLAG
5-19
*****************************************************************
CHAPTER 6
ARITHMETIC OPERATIONS
NEW INSTRUCTIONS:
ADC SBC
GENERAL.
6-1
*****************************************************************
6502 cannot even multiply or divide two numbers. The only arith-
metic operations the 6502 microprocessor can perform are
addition and subtraction. All of the other fancy operations can be
simulated by using addition and subtraction.
EXAMPLES:
CLC ;ALWAYS!
LDA #$5
ADC #$3
BRK ;PRINTS RESULT OF ADDITION
END
CLC
LDA #7
ADC #$3
BRK
END
CLC
LDA #$FC
ADC #$20
BRK
END
6-2
*****************************************************************
In the last example overflow will occur and you will end up with
the result $1C.
EXAMPLE:
CLC
LDA I
ADC J
BCS ERROR ;GO TO ERROR IF OVERFLOW
ETC... ;OTHERWISE CONTINUE PROCESSING.
EXAMPLES OF OVERFLOW:
CLC CLC
LDA #$FF LDA #$F0
ADC #$1 ADC #$20
BRK BRK
END END
6-3
*****************************************************************
CLC CLC
LDA #$80 LDA #$80
ADC #$80 ADC #$FF
BRK BRK
END END
1) Do not confuse these rules with the rules which follow for
subtraction, signed and decimal arithmetic.
3) Test for overflow with the BCS instruction. The carry flag
will be set if overflow occurs.
SUBTRACTION.
In practice, points (2) and (3) are totally opposite that of the
ADC instruction. Be aware of this! Many beginners consistently
forget that the carry must be SET before performing a subtraction,
and end up with invalid results.
6-4
*****************************************************************
the locations where I and J are supposed to be with the EQU or
EPZ pseudo opcodes.
SIGNED ARITHMETIC.
What happens when you subtract $10 (16) from $8? You
would normally expect to get -$8. The computer, however, will
give you an underflow (i.e., the carry will be cleared) since neg-
ative numbers are not allowed in the unsigned number system.
Negative numbers, despite the fact that they are not defined in
our number system, are useful on several occasions. Because of
this, a method for defining signed binary numbers had to be
developed.
6-5
*****************************************************************
EXAMPLE PROGRAMS:
CLC CLC
LDA #$80 ;-128 DECIMAL LDA #$FF ;-1 DECIMAL
ADC #$80 ;-128 DECIMAL ADC #$2 ; 2 DECIMAL
BRK ;RESULT = 0 BRK ;RESULT = 1
END END
SEC SEC
LDA #$80 ;-128 DECIMAL LDA #$FF ;-1 DECIMAL
SBC #$1 ;1 DECIMAL SBC #$1 ; 1 DECIMAL
BRK ;RESULT = +127 BRK ;RESULT=-2 ($FE)
END END
CLC SEC
LDA #$FF LDA #$23
ADC #$25 SBC #$43
BVS ERROR <- GO IF OVERFLOW -> BVS ERROR
BRK <- STOP OTHERWISE -> BRK
END END
6-6
*****************************************************************
SEC
LDA A
6-7
*****************************************************************
SBC B
BMI LBL
BVC GE
LT: <- BRANCH TO HERE IF A < B
.
.
.
.
LBL BVC LT
GE: <- BRANCH TO HERE IF A >= B
6-8
*****************************************************************
high-order decimal digit. The bit patterns 1010 thru 1111 are not
allowed in either nibble of a BCD number. With eight bits you can
represent numbers in the range of 0 thru 99. BCD digits are spec-
ified in your assembly language programs in a manner identical
to hex constants. Always preceed your BCD constants with a
dollar sign.
The decimal flag is very flexible. You can set it once and not
worry about decimal arithmetic until you manually reset the dec-
imal flag using CLD. This flexibility has one disadvantage. If you
set the decimal flag and forget to reset it, any further binary arith-
metic operations you attempt will become invalid. Although the
decimal flag can be used with great flexibility, care should be
exercised when using it. For this very reason, the first instruction
in your program should be a CLD instruction, unless, of course,
you plan to perform decimal arithmetic right away (Use SED if
so). This will initialize the decimal flag to a known state (and only
6-9
*****************************************************************
SED
SEC ;ALWAYS BEFORE A SUBTRACTION
LDA #$52 ;INIT TO DECIMAL 52
SBC #$22 ;SUBTRACT 22 (DECIMAL/BCD)
BRK ;RESULT IS 30
END
SED
CLC ;ALWAYS BEFORE AN ADDITION
LDA #$99 ;LOAD WITH 99 (DECIMAL/BCD)
ADC #$1 ;ADD 1 (DECIMAL/BCD)
BRK ;RESULT IS 0, CARRY = 1
END
SED
SEC
LDA #$00
SBC #$1
BRK ;RESULT IS 99, CARRY = 0;
END
6-10
*****************************************************************
ARITHMETIC REVIEW.
1) Maximum value
a) Signed = 127
b) Unsigned = 255
c) Decimal = 99
6-11
*****************************************************************
2) Minimum value:
a) Signed = -128
b) Unsigned = 0
c) Decimal = 0
6-12
*****************************************************************
CHAPTER 7
NEW INSTRUCTIONS:
GENERAL.
7-1
*****************************************************************
7-2
*****************************************************************
ming and top-down program design are often confused in these
articles. Structured programming entails the use of "program
structures" such as the FOR loop, the REPEAT/UNTIL loop, the
WHILE loop, the IF/THEN/ELSE statement, and BEGIN/END
(or equivalent) block structures. Obviously, structured program-
ming is not possible in assembly language because the afore-
mentioned program statements are not available in assembly lan-
guage. Sure, you can simulate those instructions using JMPs and
CMPs, but then you can simulate these statements in BASIC or
FORTRAN by using GOTO's and IF statements. The idea of
structured programming disallows the use of the GOTO (or JMP)
instruction, however, so by definition, structured programming is
not possible in assembly language. Nevertheless, it is a good
idea to simulate these programming constructs (the IF/THEN/
ELSE, REPEAT/UNTIL, etc.). This topic will be discussed later in
the chapter.
7-3
*****************************************************************
10 FOR I=1 TO 10
20 GOSUB 50
30 NEXT I
40 END
50 I=1
60 RETURN
LDX #$F
LBL JSR SETX
DEX
BNE LBL
BRK
;
;
SETX LDX #$10
RTS
7-4
*****************************************************************
This does not completely solve all of the problems with sub-
routines and register usage. Consider the following code:
XSAVE EPZ $0
LDX #$F
LBL JSR SETX
DEX
BNE LBL
BRK
;
;
SETX STX XSAVE
LDX #$10
JSR SETX2
LDX XSAVE
RTS
;
;
7-5
*****************************************************************
7-6
*****************************************************************
EXAMPLE:
XSAVE1 EPZ $0
XSAVE2 EPZ $1
;
LDX #$F
LBL JSR SETX
DEX
BNE LBL
BRK
;
;
SETX STX XSAVE1
LDX #$10
JSR SETX2
LDX XSAVE1
RTS
;
;
SETX2 STX XSAVE2
INX
LDX XSAVE2
RTS
END
7-7
*****************************************************************
EXAMPLE:
LDX #$F
LBL JSR SETX
DEX
BNE LBL
BRK
;
;
XSAVE1 DFS 1 ;RESERVE ONE BYTE FOR THE X REGISTER.
;
SETX STX XSAVE1
LDX #$10
JSR SETX2
LDX XSAVE1
RTS
;
;
XSAVE2 DFS 1
;
SETX2 STX XSAVE2
INX
LDX XSAVE2
RTS
END
7-8
*****************************************************************
LDX #$F
LBL JSR SETX
DEX
BNE LBL
BRK
;
;
SETX STX "MAGIC"
LDX #$10
JSR SETX2
LDX "MAGIC"
RTS
;
;
SETX2 STX "MAGIC"
INX
LDX "MAGIC"
RTS
END
In this example we load the X-register with the value $F and then
call SETX. At SETX we save the X-register into our magic memory
location. We then load the X-register with $10. Next the program
calls SETX2. Upon entry into SETX2 the X-register is once again
saved into our magic memory location. This causes the previous
contents to be saved somewhere else (and it's all automatic).
Next, the X-register is incremented by one, giving us $11. The
next instruction loads the X-register from our magic memory
location thus restoring $10 in the X-register. Also, this causes the
original value stored in the magic memory location ($F) to be
reloaded into the magic memory location. SETX2 then returns to
its calling procedure, namely SETX. SETX then loads the X-reg-
ister from the magic memory location (which now contains $F)
7-9
*****************************************************************
and then returns to the calling procedure with the contents of the
X-register being the same as when SETX the call invoked.
7-10
*****************************************************************
the wrap-around function causes the first byte you pushed onto
the stack to be overwritten. In general, 256 is plenty. A typical
program usually requires, at most, 64 bytes for temporary stor-
age.
EXAMPLES:
Be aware that this will destroy the contents of the 6502 accu-
mulator.
Now we can use the PHA and PLA instructions to save the
registers for us in a subroutine. This is accomplished as follows:
LDX #$F ;INIT INDEX COUNT
LBL JSR SETX
DEX ;DECREMENT COUNT
BNE LBL ;LOOP IF NOT THROUGH
BRK ;STOP
;
;
SETX PHA ;SAVE THE ACCUMULATOR
TXA ;SAVE THE X REGISTER
PHA
TYA ;SAVE THE Y REGISTER
PHA
;
LDX #$10
JSR SETX2
;
TXA ;NOW RESTORE THE Y REGISTER
TAY
PLA ;RESTORE THE X REGISTER
TAX
PLA ;RESTORE THE ACCUMULATOR
;
;
SETX2 PHA ;SAVE ACC
TXA ;SAVE X REG
PHA
TYA ;SAVE Y REG
PHA
;
INX
;
7-11
*****************************************************************
PCA
TAY ;RESTORE THE Y REG
PLA
TAX ;RESTORE X REG
PLA ;RESTORE ACC
RTS
You will notice that the registers were pulled off of the stack in the
reverse order that they were pushed onto the stack. Remember,
the stack is a LIFO (last in, first out) data structure.
EXAMPLE:
SUBRT PHA
;
LDA LOC1
CMP #$50
BLT SUB1
;
LDA #$0
STA LOC1
JMP SUBX
;
SUB1 INC LOC1
SUBX PLA
RTS
7-12
*****************************************************************
Since the X- and Y-registers were not used within this subroutine,
there was no need to save them onto the stack.
-SUBROUTINE-
-CODE GOES-
- HERE -
JSR RESTOR
RTS
;
;
SAVE PHA
TYA
PHA
TXA
PHA
RTS
;
;
RESTR PLA
TAX
PLA
TAY
PLA
RTS
END
PASSING PARAMETERS.
7-13
*****************************************************************
7-14
*****************************************************************
sums four bytes together and returns the sum of these four bytes:
LDA I
STA PARM1
LDA J
STA PARM2
LDA K
STA PARM3
LDA L
STA PARM4
JSR SUM
LDA RESULT
BRK
PARM1 DFS 1
PARM2 DFS 1
PARM3 DFS 1
PARM4 DFS 1
RESULT DFS 1
SUM CLC
LDA PARM1
ADC PARM2
CLC
ADC PARM3
CLC
ADC PARM4
STA RESULT
RTS
END
7-15
*****************************************************************
CHAPTER 8
GENERAL.
8-1
*****************************************************************
Since a conflict may arise, why even use zero page loca-
tions? After all, there are 48,496 other RAM locations which can
be used for variable storage; what's the big deal with zero page?
Well, the designers of the 6502, realizing that page zero would
be used quite often for variable storage, implemented a "zero
page addressing mode." A zero page addressing mode instruc-
tion consists of a 1-byte instruction code followed by a 1-byte
address. Since one byte can only uniquely specify 256 different
memory locations, this type of instruction can only reference one
page of memory. You got it: page zero. Thus, a zero page instruc-
tion, since it only requires two bytes, saves you some memory
(remember, absolute addressing mode instructions require three
bytes). Another advantage to using zero page instructions is that
zero page instructions execute faster than absolute addressing
mode instructions (in fact a zero page addressing mode instruc-
tion executes in three-fourths the time required by an absolute
addressing mode instruction).
EXAMPLE:
LDA $1
STA $FF
LDX $1
ADC $25
EXAMPLE:
LBL EPZ $0
LBLA EQU $0
LDA LBL ;ZERO PAGE ADDRESSING USED
LDA LBLA ;ABSOLUTE ADDRESSING USED
8-2
*****************************************************************
Single variables are nice, but often strings and arrays are
required to accomplish a desired task. An array is a collection of
data, each element of the array being of identical length (i.e., the
same data type) to every other element. Arrays are stored in
consecutive memory locations and any element of an array can
be accessed by adding a displacement to the address of the first
element.
This ensures that the memory space for ARRAY2 does not con-
flict with the memory space for ARRAY1
8-3
*****************************************************************
8-4
*****************************************************************
ORG $800
JMP START
ARRAY ORG *+$100 ;RESERVE 256 LOCATIONS
START --- ;CODE GOES HERE
8-5
*****************************************************************
ORG $800
JMP LBL
ARRAY ORG *+$100
LBL LDA ARRAY
STA ARRAY+$1
BRK
END
and then moved it to location $4000 before running it, the program
would not execute as planned. Since the program was ORG'd for
location $800, all absolute addresses (such as the address of
LBL and ARRAY) will simply be offsets from this initial address.
In this case ARRAY will be assigned the address $803 and LBL
will be assigned the address $903. Now, when the code is gen-
erated for this program, $903 will be substituted for LBL, which
means the first JMP instruction will be converted to JMP $903.
If you move the code to location $4000 and try to execute it with
the 4000G monitor command, the first instruction (JMP $903) will
simply continue execution of the program at the location at which
the program was originally assembled.
8-6
*****************************************************************
not only will the code be assembled to run at location $4000, but
it will also be stored there. Since this is a no-no location for object
code (LISA normally stores the textfile in this region and storing
object code here may "clobber" part of your textfile) some means
must be used to make ensure that the object code gets stored in
the range $800-$1800.
The OBJ pseudo opcode does this for you. The OBJ pseudo
opcode simply changes the value in the code counter (the pointer
to where the object code is being stored) to whatever address
appears in the operand field. If you want to assemble your pro-
gram to run at location $4000, you should use the code:
ORG $4000
OBJ $800
LDA #$0
STA LBL
BRK
LBL EQU $0
END
ORG $4000
OBJ $800
JMP LBL
ARRAY ORG *+$100
LBL LDA ARRAY
STA ARRAY+$1
BRK
END
The ORG $4000 pseudo opcode insures us that the correct code
will be generated. The OBJ $800 pseudo opcode ensures that
the code will be stored in the memory range $800 - $1800. The
JMP instruction ensures that the data will not get interpreted as
instruction code. BUT, the ORG *+$100, used to reserve memory
for the array, causes a slight problem. Remember, the ORG
pseudo opcode resets the location counter as well as the code
counter. This means that when "ORG *+$100" is encountered,
The location counter will be set to $4103 (which we want), but the
code counter will also be set to $4103 (which we don't want),
thereby clobbering the textfile. Including the instruction "OBJ
*+$100" does not complete solve the problem either. Since the
location counter is already $4103 by the time the OBJ *+$100
would get executed, the inclusion of such an instruction would be
8-7
*****************************************************************
ORG $4000
OBJ $800
JMP LBL
ARRAY DFS $100
LBL LDA ARRAY
STA ARRAY+$1
BRK
END
JMP LBL
ARRAY HEX 00010203
LBL LDA ARRAY
STA $0
BRK
END
8-8
*****************************************************************
JMP LBL
ARRAY ASC "HI THERE"
LBL LDA ARRAY
JSR $FDED
LDA ARRAY+1
JSR $FDED
LDA ARRAY+2
JSR $FDED
LDA ARRAY+3
JSR $FDED
LDA ARRAY+4
JSR $FDED
LDA ARRAY+5
JSR $FDED
LDA ARRAY+6
JSR $FDED
LDA ARRAY+7
JSR $FDED
BRK
END
In case you are wondering, this program prints the message "HI
THERE" on the video screen (without the quotes). The ASCII
string following the ASC pseudo opcode must be enclosed in
quotes or apostrophes. For now, always use the quotes. The use
of the apostrophe will be discussed later.
EXAMPLE:
8-9
*****************************************************************
signal to LISA that the quote is not actually a delimiter, but the
quote character. The above example would generate the following
charters in memory:
HOW'S "THIS"
Now that you can reserve memory for array storage, how
are array elements accessed? Accessing individual elements is
very easy. You use the address of the first element of an array
and add in a displacement to this address. For example, if you
want to access the tenth element of the array ARRAY, you would
use ARRAY+$9 as your address (remember, arrays in assembly
language start with an index of 0). Several of the previous exam-
ples have used this method for accessing array elements. This,
however, is a static displacement, meaning that the address
remains constant at run time, and is calculated only at assembly
time. Thus, if you try a statement of the form;
LDA ARRAY+I
8-10
*****************************************************************
EXAMPLES:
LDA LBL,X
STA ARRAY,X
ADC $1,X
SBC $FFFF,X
EXAMPLES:
LDA LBL,Y
STA ARRAY,Y
ADC $1,Y
SBC $FFFF,Y
8-11
*****************************************************************
LDX #$1
LDA ARRAY,X ;LOADS ACC FROM LOCATION ARRAY+$1
LDY #$FF
STA STRING,Y ;STORES ACC AT LOCATION STRING+$FF
Since the X- and Y-registers are only eight bits long, you are
limited to a range of 256 bytes when using the indexed addressing
modes. For most applications this is sufficient (and, in fact, for
8-12
*****************************************************************
LDA LOCADR,X
STA JMPADR
INX
LDA LOCADR,X
STA JMPADR+$1
JMP (JMPADR)
JMPADR DFS 2 ;RESERVE TWO BYTES FOR JMPADR
LOCADR HEX 0008 ;ADDRESS TABLE IN BYTE
HEX 0009 ;REVERSEE ORDER
HEX 0010
END
8-13
*****************************************************************
8-14
*****************************************************************
CPX #0
BNE LBL0
JMP $800
LBL0 CPX #2
BNE LBL1
JMP $900
LBL1 JMP $1000
After all, the latter method takes less memory and seems much
simpler to program. For this simple example, yes, the latter
method is probably better, but keep in mind, for every additional
jump you wish to handle, you need to add seven bytes to the
latter version (a compare, a branch, and a jump instruction) and
only two bytes to the former program segment (an address). The
break-even point (in terms of code) is between four and five
jumps.
As before, this method works quite well if there are only a few
types of output devices. However, there are two problems with
this method. First, you cannot anticipate all the devices which will
8-15
*****************************************************************
How does using the indirect jump solve the two aforemen-
tioned problems? The first problem (not knowing which devices
will eventually be used with the computer) is not a problem at all.
The indirect jump I/O handler takes all devices into account. If
you wish to output data to some new type of device, all you need
to do is load the address of the device handler into locations $36
and $37.
8-16
*****************************************************************
This program should look familar; it's the memory clear routine
which we used earlier with the straight indexed by X and Y
addressing modes.
8-17
*****************************************************************
8-18
*****************************************************************
CHAPTER 9
NEW INSTRUCTIONS:
GENERAL.
9-1
*****************************************************************
one output value. Since Boolean values are either false or true
(represented by zero or one respectively), single input functions
can produce only one of two outputs. Likewise, two input Boolean
functions can produce one of four output values (not necessarily
different).
COMPLEMENT FUNCTION.
----------------------
Input bit Output bit
----------------------
A X
--- ---
0 1
1 0
Table 9-1 (above) simply states that if you give the comple-
ment function a value "A" of 0, you will be returned a value "X"
of 1. Conversely, if you pass the complement function a value "A"
of 1 then you will be returned a value "X" of 0. The complement
function is sometimes called the 'NOT' function (i.e., NOT TRUE
is false and NOT FALSE is true), and sometimes it is called the
"one's complement."
AND FUNCTION.
9-2
*****************************************************************
OR FUNCTION.
*****************************************************************
1 0 1
1 1 1
EXCLUSIVE-OR FUNCTION.
9-4
*****************************************************************
AND INSTRUCTION.
EXAMPLES:
9-5
*****************************************************************
The N flag is set if bit seven of the result is one. The zero
flag is set if the result of the AND operation is zero. One inter-
esting use of the AND instruction is to test to see if a bit is set or
not. For instance, if you want to see if bit zero of the accumulator
is set, simply AND the accumulator with the value $1. If bit zero
is set (i.e. one), the accumulator will contain one after the AND
operation, and likewise the Z flag will be reset, so you can use
the BNE instruction to test for this. If bit zero of the accumulator
is not set, the accumulator will contain zero after the AND oper-
ation and the BNE test will fail. To test whether or not a memory
location contains a zero or one in bit zero, load the accumumator
with the constant $1 and then AND the accumulator with that
memory location.
The bit test feature of the AND instruction is very useful,
except that the contents of the accumulator are modified. When
performing simple bit tests, it is sometimes convenient to leave
the contents of the accumulator alone. This can be accomplished
by the use of the BIT (BIt Test) instruction. The BIT instruction
AND's the accumulator with an absolute or zero page memory
location (only!) and the results of this AND operation are used to
set the N, Z, and V flags. The flags are set according to the
following rules:
ORA INSTRUCTION.
EXAMPLES:
9-6
*****************************************************************
XOR/EOR INSTRUCTION.
EXAMPLES:
EXAMPLES:
9-7
*****************************************************************
MASKING OPERATIONS.
MASKING OUT.
*****************************************************************
high-order (H.O.) four bits and the low-order (L.O.) nibble in the
low-order four bits.
EXAMPLE:
TO TEST BIT #0
LDA BITS
AND #%1
BTR THERE
TO TEST BIT #1
LDA BITS
AND #%10
BTR THERE
TO TEST BIT #2
LDA BITS
AND #%100
BTR THERE
TO TEST BIT #3
LDA BITS
AND #%1000
BTR THERE
9-9
*****************************************************************
TO TEST BIT #4
LDA BITS
AND #%10000
BTR THERE
TO TEST BIT #5
LDA BITS
AND #%100000
BTR THERE
TO TEST BIT #6
LDA BITS
AND #%1000000
BTR THERE
TO TEST BIT #7
LDA BITS
AND #%10000000
BTR THERE
9-10
*****************************************************************
EXAMPLE:
9-11
*****************************************************************
EXAMPLE:
EXAMPLE:
9-12
*****************************************************************
MASKING IN.
The 6502 ORA instruction can be used to force bits on. This
feature allows us to set a particular value in our bit array to true.
The code to perform this operation is:
EXAMPLE:
LDA BYTE1
ORA BYTE2
ORA BYTE3
ORA BYTE4
. .
. .
. .
ORA BYTEn
BEQ ALLZER
9-13
*****************************************************************
The arithmetic shift left instruction shifts all the bits in the
accumulator one position to the left. Bit zero is shifted into bit
one, bit one is shifted into bit two, bit two is shifted into bit three,
etc. A zero is shifted into bit zero, and bit seven is shifted into the
carry flag. The 6502 mnemonic for arithmetic shift left is 'ASL.'
When shifting the contents of the 6502 accumulator, this instruc-
tion does not have an operand.
EXAMPLE OF ASL:
LDA VALUE
ASL
ASL ;FOUR SHIFTS MOVE THE L.O.
ASL ;FOUR BITS INTO THE H.O.
ASL ;FOUR BITS (L.O. FOUR BITS
STA VALUE ;BECOME ZERO)
Since the carry out of bit seven ends up in the carry flag, you can
use the BCC and BCS instructions to test for a 'shift overflow.'
Note (as demonstrated in the example) that the ASL instruction
9-14
*****************************************************************
ASL
7 6 5 4 3 2 1 0
-------------------------------
C <- | | | | | | | | | <- 0
-------------------------------
only shifts one bit. If you need to shift more than one bit position,
you must execute several ASL instructions.
LSR
7 6 5 4 3 2 1 0
-------------------------------
0 -> | | | | | | | | | -> C
-------------------------------
nibble into the first byte. Just load the accumulator from the mem-
ory location containing the BCD value, then AND the accumulator
with $F and store the result in the first byte.
EXAMPLE:
LDA VALUE
AND #$F
STA LOC1
AND'ing the value with $F0 to get the high-order byte is not
entirely satisfactory, because the value we desire will still be in
the high-order nibble of the accumulator. By using the LSR
instruction, this data can be moved down into the low-order four
bits of the accumulator, at which point the data can be stored in
the second byte of the destination address.
EXAMPLE:
LDA VALUE
AND #$F0
LSR
LSR
LSR
LSR
STA LOC1+$1
9-15
*****************************************************************
Since zeros are automatically shifted into bit number seven, after
the four LSR instructions are executed, the accumulator would
have all zeros in the high-order nibble anyway, so there is no need
for the extra AND #$F instruction. BETTER CODE:
LDA VALUE
LSR
LSR
LSR
LSR
STA LOC1+$1
LDA VALUE
AND #$F
STA LOC1
LDA VALUE
LSR
LSR
LSR
LSR
STA LOC1+$1
ROL
7 6 5 4 3 2 1 0
-------------------------------
--| | | | | | | | |<-
| ------------------------------- |
| |
| --- |
----------------->| C |--------------
---
row, you end up with the value you started with in the accumulator.
The 6502 mnemonic for the rotate left instruction is ROL.
9-16
*****************************************************************
ROR
7 6 5 4 3 2 1 0
-------------------------------
->| | | | | | | | |--
| ------------------------------- |
| |
| --- |
------------------| C |<-------------
---
with the ROL instruction, after nine rotates you end up with the
value you started with in the accumulator.
Until now, all shifts and rotates have only been used with the
6502 accumulator. The 6502 shift and rotate instructions can also
be used to shift or rotate data in memory locations, effectively
bypassing the accumulator (this is similar in operation to the INC
and DEC instructions). If the operand field is not blank (which is
required for the accumulator addressing mode), the operand field
will be assumed to contain an absolute (or zero page) memory
address. The contents of this memory location will be shifted or
rotated with the same results as would be obtained if the accu-
mulator had been operated upon. The indexed by X addressing
mode is also available.
EXAMPLES:
9-17
*****************************************************************
EXAMPLES:
2) MULTIPLY ACC BY 32
ASL ;TIMES 2
ASL ;TIMES 4
ASL ;TIMES 8
ASL ;TIMES 16
ASL ;TIMES 32
EXAMPLE:
ASL
BCS ERROR
ASL
BCS ERROR
ASL
BCS ERROR
ASL
BCS ERROR
9-18
*****************************************************************
tion problem down into several distinct steps and adding the
results of these intermediate steps together. For example, multi-
plying the accumulator by three could be broken down into a
multiplication by two and a multiplication by one (which is the
original value itself).
EXAMPLE:
MULTIPLICATION BY THREE
STA TEMP ;MAKE A TEMPORARY COPY
ASL ;MULTIPLY ACC BY TWO
CLC ;ADD IN THE ORIGINAL VALUE
ADC TEMP ;TO GET 2xACC + ACC = 3xACC
EXAMPLE:
MULTIPLICATION BY SIX
EXAMPLE:
MULTIPLICATION BY TEN
9-19
*****************************************************************
But BCD is not the only case where data packing is per-
formed. Some situations may require three data fields within a
single byte. For example, you may have a Boolean value in bit
seven; an Apple slot number in bits four, five, and six; and a hex
value in the range $0-$F in the low-order nibble. Getting at the 4-
bit value is easy, just AND the accumulator with $F. Getting at the
three bits in the middle of the data structure is a little more com-
plicated. First, you must shift the accumulator to the left four bits
to right-justify the data field and to eliminate the low-order four
bits. Next, the accumulator has to be AND'ed with $7 to eliminate
the Boolean value and preserve the low-order three bits.
EXAMPLE:
LDA VALUE
LSR ;SHIFT RIGHT FOUR TIMES
LSR ;TO RIGHT JUSTIFY FIELD
LSR ;AND ELIMINATE L.O.
LSR ;NIBBLE
AND #%0111 ;MASK OUT BOOLEAN VALUE
9-20
*****************************************************************
EXAMPLE:
LDA VALUE
AND #$80
ASL
ROL
Obviously, if you just want to test the boolean value, you do not
need to right-justify it. You need only to use the BTR/BFL instruc-
tions after the AND #$80 (or even the BMI/BPL instructions after
the LDA value).
9-21
*****************************************************************
EXAMPLE:
9-22
*****************************************************************
CHAPTER 10
MULTIPLE-PRECISION
OPERATIONS
GENERAL.
Until now, all operations utilized have worked with only eight
bits. For some operations this is fine. For others, being limited to
eight bits is intolerable. Nevertheless, the 6502 is limited to work-
ing with eight bits at a time. In order to handle data types of larger
sizes, such as 16-bit integers and 32-bit floating point numbers,
we must break them up into several 8-bit operations. For example,
a 16-bit addition is handled as two 8-bit additions.
LDA A
AND B
STA C
LDA A+$1
AND B+$1
STA C+$1
10-1
*****************************************************************
EXAMPLES:
LDA A
ORA B
STA C
LDA A+$1
ORA B+$1
STA C+$1
LDA A
XOR B
STA C
LDA A+$1
XOR B+$1
STA C+$1
10-2
*****************************************************************
ASL LOBYTE
ROL HOBYTE
In this case, the carry out of the low-order byte ends up in the
carry flag, and then the second instruction (ROL) shifts the carry
flag into the low-order bit of the high-order byte (just as we
expect). Naturally, the high-order bit ends up in the carry flag. A
three-byte ASL can be manufactured by tacking another ROL
instruction onto the end of this sequence:
ASL BYTE
ROL BYTE+$1
ROL BYTE+$2
MULTIPLE-PRECISION
SHIFTS AND ROTATES
TWO-BYTE ASL
ASL
7 6 5 4 3 2 1 0
-------------------------------
--| | | | | | | | | <- 0
| -------------------------------
| LOW-ORDER BYTE
|
| ---
----------------->| C |--------------
--- |
ROL |
7 6 5 4 3 2 1 0 |
------------------------------- |
--| | | | | | | | |<-
| -------------------------------
| HIGH-ORDER BYTE
| ---
----------------->| C |
---
10-3
*****************************************************************
LSR
7 6 5 4 3 2 1 0
-------------------------------
0 -> | | | | | | | | | -
------------------------------- |
HIGH-ORDER BYTE |
--- |
------------------| C |<-------------
| ---
| ROR
| 7 6 5 4 3 2 1 0
| -------------------------------
->| | | | | | | | |--
------------------------------- |
LOW-ORDER BYTE |
--- |
| C |<-------------
---
shifted into the high- order bit and then the low-order bit gets
shifted into the carry flag. A 2-byte LSR would be coded as:
LSR BYTE+$1
ROR BYTE
LSR BYTE+$2
ROR BYTE+$1
ROR BYTE
MULTIPLE-PRECISION
ROTATE-LEFT SEQUENCES.
ROL BYTE
ROL BYTE+$1
10-4
*****************************************************************
MULTIPLE-PRECISION
SHIFTS AND ROTATES
TWO-BYTE ROL
ROL
7 6 5 4 3 2 1 0
------------------------------- ---
--| | | | | | | | | <- | C |
| ------------------------------- ---
| LOW-ORDER BYTE
|
| ---
----------------->| C |--------------
--- |
ROL |
7 6 5 4 3 2 1 0 |
------------------------------- |
--| | | | | | | | |<-
| -------------------------------
| HIGH-ORDER BYTE
| ---
----------------->| C |
---
The carry is shifted into bit zero, as we expect; bit seven is shifted
into the carry, and then into bit eight because of the second ROL
instruction. Finally, bit fifteen is shifted into the carry flag thereby
performing a 16-bit ROL. A 3-byte ROL instruction is written:
ROL BYTE
ROL BYTE+$1
ROL BYTE+$2
MULTIPLE-PRECISION ROTATE-RIGHT
SEQUENCES.
ROR BYTE+$1
ROR BYTE
TWO-BYTE ROR
ROR
7 6 5 4 3 2 1 0
--- -------------------------------
| C | -> | | | | | | | | | -
--- ------------------------------- |
HIGH-ORDER BYTE |
--- |
------------------| C |<-------------
| ---
| ROR
| 7 6 5 4 3 2 1 0
| -------------------------------
->| | | | | | | | |--
------------------------------- |
LOW-ORDER BYTE |
--- |
| C |<-------------
---
10-5
*****************************************************************
ROR BYTE+$2
ROR BYTE+$1
ROR BYTE
MULTIPLE-PRECISION UNSIGNED
ARITHMETIC.
As you may recall, you can check the carry flag after an
addition; it will be set if an overflow (into the "ninth" bit) occurred
and reset otherwise. As such, we can use the carry flag as our
ninth bit when performing arithmetic. Fine, but how is this going
to allow us to perform arithmetic on 16, 24, or 32 bits? Remember
our rules for unsigned addition? One rule states that the carry
must be cleared before the addition takes place, because the
carry flag gets added in as part of the operand. This means that
if the carry flag is set, then you do not end up with the sum of the
accumumator and the operand, but rather you get the sum of the
accumulator and the operand plus one. Naturally, if the carry is
clear, you get the sum of the accumulator and the operand plus
10-6
*****************************************************************
the carry flag (which is zero), thus giving you the true sum.
LDA OP1+$1
ADC OP2+$1
STA RESULT+$1
Note that the carry flag is not cleared between the additions here!
Remember, the carry flag contains vital information for successful
multiple-precision addition. A 3-byte addition operation would be
coded:
CLC
LDA OP1
ADC OP2
STA RESULT
LDA OP1+$1
ADC OP2+$1
STA RESULT+$1
LDA OP1+$2
ADC OP2+$2
STA RESULT+$2
4) Add the second, third, ..., nth pairs of bytes together and
store the results. Do not clear the carry flag before these
additions.
5) After the nth addition, the carry flag will be set if an over-
flow occurred, otherwise the carry flag will be cleared.
10-7
*****************************************************************
MULTIPLE-PRECISION UNSIGNED
SUBTRACTION.
SEC ;ALWAYS!
LDA OPRND1 ;GET L.O. BYTE OF OPERAND #1
SBC OPRND2 ;SUBTRACT L.O. BYTE OF OPERAND #2
STA RESULT ;SAVE IN L.O. BYTE OF RESULT
LDA OPRND1+$1 ;GET H.O. BYTE OF OPERAND #1
SBC OPRND2+$1 ;SUBTRACT H.O. BYTE OF OPERAND #2
STA RESULT+$1 ;SAVE IN H.O. BYTE OF RESULT
BCC ERROR ;TEST FOR OVERFLOW
4) Subtract the second, third, ..., nth bytes and store the
results.
5) After the nth bytes are subtracted, the carry will be clear
if underflow occurred. If the carry flag is set, then no
overflow occurred.
10-8
*****************************************************************
MULTIPLE-PRECISION INCREMENTS.
10-9
*****************************************************************
INC LOC
BNE LBL
INC LOC+$l
LBL:
INC LOC
BNE LBL
INC LOC+$1
BNE LBL
INC LOC+$2
LBL:
MULTIPLE-PRECISION DECREMENTS.
10-10
*****************************************************************
LDA OPRND
BNE LBL1
LDA OPRND+$1
BNE LBL2
DEC OPRND+$2
LBL2 DEC OPRND+$1
LBL1 DEC OPRND
MULTIPLE-PRECISION UNSIGNED
COMPARISONS.
EXAMPLE:
LDA TSTZER
ORA TSTZER+$1
BEQ ISZERO
10-11
*****************************************************************
The test for equality is not quite as simple. This test must
be handled in two parts. First, the low-order bytes are compared.
If they are not equal, then a branch should be taken to some
location further on in the code stream. If they are equal, you
should drop down and compare the high-order bytes. If the high-
order bytes are not equal a branch should be taken to the same
location as in the previous branch. If the second test for inequality
fails you know that the two operands are equal. The following
code will jump to EQUALS if the two operands specified are equal,
or it will jump to NOTEOL if the operands specified are not equal.
LDA OPRND1
CMP OPRND2
BNE NE
LDA OPRND1+$1
CMP OPRND2+$1
BNE NE
JMP EQUALS
NE JMP NOTEQL
LDA OPRND1
CMP OPRND2
BNE NE
LDA OPRND1+$1
CMP OPRND2+$1
BEQ EQL
NE JMP NOTEQL
EQL:
10-12
*****************************************************************
LDA OPRND1
CMP OPRND2
BNE NE
LDA OPRND1+$1
CMP OPRND2+$1
BNE NE
LDA OPRND1+$2
CMP OPRND2+$2
BEQ EQL
NE JMP NOTEQL
EQL:
The inequalities (<, <=, >, & > =) turn out to be easier to
program than the test for equals/not equals. If you will remember
the discussion of the CMP instruction, it was mentioned that this
instruction is really nothing more than a subtraction. The only
difference is that the result is not kept around. Since a straight
subtraction is performed (as opposed to a subtract with carry), a
mutiple-precision CMP instruction is not technically possible. It
can be simulated, however, by the combined use of the CMP and
SBC instructions. The CMP instruction is used to compare the
low-order bytes (this instruction is used so that the carry does not
have to be explicitly set), and then the SBC instructon is used to
compare successive bytes. After the last bytes are compared
(using SBC) the BGE (or BCS) and BLT (or BCC) instructions
may be used to test the result.
EXAMPLES:
X>=Y
----
LDA X
CMP Y
LDA X+1
SBC Y+1
BGE GE
X<Y
----
LDA X
CMP Y
LDA X+1
SBC Y+1
BLT LT
10-13
*****************************************************************
X >= Y
------
LDA X
CMP Y
LDA X+$1
SBC Y+$1
BGE THERE
When you say that X is greater than or equal to Y, you are also
stating that Y is less than or equal to X, so the above comparison
is also testing to see if Y is less than or equal to X. To perform
the comparison X<=Y, use the code:
X <= Y
------
LDA Y
CMP X
LDA Y+$1
SBC X+$1
BGE THERE
SIGNED COMPARISONS.
First, to test for equals, not equals, zero, or minus, you use
the same tests as you would for an unsigned value. Testing for
the inequalities '<=','<', '>= not as straight forward. Without a
lengthy discussion of the 6502 hardware and two's complement
10-14
*****************************************************************
X >= Y
------
SEC
LDA X
SBC Y
LDA X+$1
SBC Y+$1
BVS LBL1
BMI LT
LBL2 JMP GTREQL
LBL1 BPL LBL2
LT:
X <= Y
------
SEC
LDA Y
SBC X
LDA Y+$1
SBC X+$1
BVS LBL1
BMI GT
LBL2 JMP LESEQL
LBL1 BPL LBL2
GT:
X < Y
------
SEC
LDA X
SBC Y
LDA X+$1
SBC Y+$1
BVS LBL1
BPL GE
LBL2 JMP LESS
LBL1 BMI LBL2
GE:
10-15
*****************************************************************
X <= Y
------
SEC
LDA Y
SBC X
LDA Y+$1
SBC X+$1
BVS LBL1
BPL
LBL2 JMP LESEQL
LBL1 BMI LBL2
LT:
10-16
*****************************************************************
CHAPTER 11
BASIC I/O
GENERAL.
CHARACTER OUTPUT.
11-1
*****************************************************************
LDA #"A"
JSR $FDF0
LDA #"B"
JSR $FDF0
LDA #"C"
JSR $FDF0
RTS
END
11-2
*****************************************************************
LDX #$0
LOOP INX
LDA STRING,X
JSR $FDF0
CPX STRING
BLT LOOP
RTS
;
STRING STR "I WON! CARE TO PLAY AGAIN?"
END
LDX #$0
LOOP CPX STRING
BGE EXIT
LDA STRING+$1,X
JSR $FDF0
INX
JMP LOOP
EXIT RTS
STRING STR "I WON! CARE TO PLAY AGAIN?"
END
11-3
*****************************************************************
11-4
*****************************************************************
Note that the ASC pseudo opcode was used rather than the
STR pseudo opcode. Remember, the STR pseudo opcode out-
puts a length byte before it outputs the string. This feature is not
desirable here.
LDX #$0
LOOP LDA STRING,X
BEQ EXIT
JSR $FDF0
INX
JMP LOOP
;
EXIT RTS
STRING ASC "I WON! CARE TO PLAY AGAIN?"
BYT $8D
ASC "(Y/N):"
BYT $0
END
LDX #$0
LOOP LDA STRING,X
BEQ EXIT
JSR $FDF0
INX
BNE LOOP
;
EXIT RTS
STRING ASC "> 255 CHARACTERS HERE"
BYT $0
END
11-5
*****************************************************************
PRTSTR STA $0
STY $1
LDY #$0
LOOP LDA ($0),Y
BEQ EXIT
JSR $FDF0
INY
BNE LOOP
INC $1
BNE LOOP
;
EXIT RTS
11-6
*****************************************************************
Example:
LDA #STRING
LDY /STRING
JSR PRTSTR
RTS
STRING ASC "STRING OF ANY LENGTH"
BYT $0
END
Now only three lines of code (plus the string) are required
to output a string of characters. That's quite a bit better than the
seven to ten lines required by the other methods. Nevertheless,
this method has two drawbacks. First, three lines are still two
lines more than one. Second, this method requires that data be
passed to the subroutine in the accumulator and Y-register. Typ-
ically, one likes to avoid the use of the registers for parameter
passing as much as possible (since the registers are much more
useful for indexing and counter purposes).
JSR PRINT
ASC "HELLO THERE"
HEX 00
RTS
END
11-7
*****************************************************************
EXAMPLES:
JSR PRINT
ASC "I WON! CARE TO PLAY AGAIN?"
BYT $8D
ASC "(Y/N):"
BYT $0
.
.
.
JSR PRINT
ASC "HELLO THERE, HOW ARE YOU!"
BYT $0
JSR PRINT
BYT $8D
ASC "I AM A SMART COMPUTER!"
BYT $0
.
.
ETC....
11-8
*****************************************************************
11-9
*****************************************************************
-SIMULATION OF A PR#3
LDA #$00
STA $36
LDA #$C3
STA $37
11-10
*****************************************************************
-SIMULATION OF A PR#0
LDA #$FDF0
STA $36
LDA /$FDF0
STA $37
ORG $300
LDA #DBLVSN
STA $36
LDA /DBLVSN
STA $37
RTS
;
DBLVSN JSR $FDF0
JMP $FDF0
END
CHARACTER INPUT.
11-11
*****************************************************************
You will notice that any key read in this manner will not be
'echoed' onto the Apple screen. To perform this function (that of
an 'electronic typewriter'), use the following code:
11-12
*****************************************************************
When using the Apple keyboard and the video display, the
Apple monitor provides a very handy character input subroutine.
It is located at $FD0C and it sets the current cursor location to
the flashing mode. Upon keyboard entry the flashing cursor is
replaced with the data originally under the cursor. A better 'elec-
tronic typewriter' might be:
The routine at location $FD0C does not 'echo' the character back
to the display, hence the JSR COUT.
11-13
*****************************************************************
Luckily, a line input routine has already been written for us.
The address of this routine is $FD67 and it is called, "GETLNZ."
When called, it outputs a carriage return, prints a 'prompt' char-
acter (more on that later), and then reads a line of text from the
current input device. Whatever character resides in location $33
is used as a prompt character, so, if you wish to use a new and
unique prompt (perhaps ":" or "-" or "="), simply store the char-
acter at location $33 before calling GETLNZ.
11-14
*****************************************************************
CHAPTER 12
NUMERIC I/O
GENERAL.
1) Hexadecimal I/O
2) Byte/numeric I/O
HEXADECIMAL OUTPUT.
12-1
*****************************************************************
The CMP #$BA is required because the letter A does not imme-
diately follow the digit 9 in the ASCII character set. Since BLT is
the same as BCC, the processor is guaranteed to have the carry
flag set if the ADC #$6 is encountered. In effect, we are adding
seven to the contents of the accumulator. $BA plus $7 is $C1
which is the ASCII code for the letter A, exactly what we want.
12-2
*****************************************************************
Hex numbers are fine for computer type people, but when
trying to present information to others, the decimal number sys-
tem should be used. The monitor does not contain a facility for
outputting decimal numbers (except BCD) so we will have to write
one ourselves. In this section, a method for outputting a single
byte as an unsigned integer in the range 0 to 255 will be explored.
12-3
*****************************************************************
To use this routine, load into the location VALUE the byte to
be printed; then JSR PRTBYT. The decimal number correspond-
ing to the byte stored in location VALUE will be output to the
screen (or other output device).
12-4
*****************************************************************
12-5
*****************************************************************
T10H HBY !1
HBY !10
HBY !100
HBY !1000
HBY !10000
;
COUT EQU $FDED
LEAD0 EPZ $0
DIGIT EPZ LEAD0+S1
VALUE EPZ DIGIT+$1
END
12-6
*****************************************************************
12-7
*****************************************************************
NUMERIC INPUT.
12-8
*****************************************************************
12-9
*****************************************************************
;
BCDONE RTS
12-10
*****************************************************************
4) When all the digits have been shifted in, the 16-bit val-
contained in the two memory locations is the binary con-
tained in the two memory locations is the binary repre-
sentation of the decimal value.
12-11
*****************************************************************
MUL100
PHP
PHA
ASL VALUE ;MULTIPLY VALUE BY 2
ROL VALUE+$1
LDA VALUE+$1 ;SAVE A COPY OF VALUE
PHA ;MULTIPLIED BY 2
LDA VALUE
ASL VALUE ;NOW MULTIPLY VALUE BY 8
ROL VALUE+$1 ;SINCE VALUE HAS ALREADY
ASL VALUE ;BEEN MULTIPLIED BY 2
ROL VALUE+$1 ;A SIMPLE MULTIPLY BY 4 GIVES
CLC
ADC VALUE ;ADD IN 2xVALUE TO 8xVALUE
STA VALUE ;TO OBTAIN 1OxVALUE
PLA
ADC VALUE+$1
STA VALUE+$1
PLA
PLP
RTS
The final step in the algorithm (adding in the digit to the 16-
bit number) is trival at this point. The final decimal input routine
could be:
12-12
*****************************************************************
12-13
*****************************************************************
This routine does suffer from a few drawbacks. First, it does not
check for overflow. Second, it terminates entry upon the first non-
digit encountered, which means that bad data entries will go
undetected. Finally, if the first character encountered is not a dec-
imal digit, the routine immediately returns and zero is returned in
value.
12-14
*****************************************************************
12-15
*****************************************************************
BCS MOVRFL
ASL VALUE
ROL VALUE+$1
BCS MOVRFL
CLC
ADC VALUE
STA VALUE
PLA
ADC VALUE+$1
STA VALUE+S1
BCS MOVRFL
PLA
BIT ;SET V FLAG TO ZERO
RTS
MOVRFL
BIT OVERFL ;SET V FLAG TO ONE
RTS
;
NOVRFL HEX 00
OVERFL HEX 40
;
;
; TSTDEC: TESTS CHARACTER IN ACC TO SEE IF IT IS
; A VALID DECIMAL DIGIT
; CARRY IS SET IF IT IS
;
TSTDEC:
CMP #"0"
BLT NOTDEC
CMP #9"+$1
BGE NOTDEC
SEC
RTS
;
NOTDEC CLC
RTS
;
;
INPUT EQU $200
VALUE EPP $0
;
GETLN EQU $FD67
;
; NOTE: THE PRINT ROUTINE PROVIDED IN THE PREVIOUS
; CHAPTER MUST BE INCLUDED HERE
;
;
To use this routine, read a line of data using GETLN. Set up the
X-register so that it points to the desired decimal digits to be input
(leading blanks allowed) and then JSR DECINP. Upon returning
from DECINP the desired number (in binary form) will be stored
in VALUE and VALUE+$1. There are some improvements you
may want to make to this basic routine, such as:
12-16
*****************************************************************
12-17
*****************************************************************
;
SGNOVR JSR PRINT
HEX 8D
ASC ">32767 REENTER"
HEX 8D00
JSR GETLN
LDX #$0
JMP DOSGN
SIGN EPZ VALUE+$2
12-18
*****************************************************************
CHAPTER 13
MULTIPLICATION AND
DIVISION
GENERAL.
MULTIPLICATION.
10110
x 110
-----
13-1
*****************************************************************
13-2
*****************************************************************
13-3
*****************************************************************
EXMPL:
LDA #!25 ;25 DECIMAL
STA MULPLR
LDA /!25 ;H.O. BYTE OF 25
STA MULPLR+$1
LDA #!66
STA MULOND
LDA /!66
STA MULCND+$1
LDA #$0 ;MUST SET PARTIAL TO ZERO
STA PARTIAL
STA PARTIAL+$1
JSR USMUL ;PERFORM THE MULTIPLICATION
LDA MULPLR ;MOVE PRODUCT TO RESULT
STA RESULT
LDA MULPLR+$1
STA RESULT+$1
ETC...
13-4
*****************************************************************
;
USMUL1 LDY #$18 ;SET UP FOR 24-BIT MULTIPLY
USMUL2 LDA MULPLR ;TEST L.O. BIT TO SEE IF SET
LSR
BCC USMUL4
;
CLC ;L.O. BIT SET, ADD MULCND TO
LDA PARTIAL ;PARTIAL PRODUCT
ADC MULCND
STA PARTIAL
LDA PARTIAL+$1
ADC MULCND+$1
STA PARTIAL+$1
LDA PARTIAL+$2
ADC MULCND+$2
STA PARTIAL+$2
;
; SHIFT RESULT INTO MULPLR AND GET THE NEXT BIT
; OF THE MULTIPLIER INTO THE LOW ORDER BIT OF
; MULPLR
;
USMUL4 ROR PARTIAL+$2
ROR PARTIAL+$1
ROR PARTIAL
ROR MULPLR+$2
ROR MULPLR+$1
ROR MULPLR
;
; SEE IF DONE YET
;
DEY
BNE MUL2
PLA
TAY
PLA
RTS
;
;
MULPLR EPZ $50
PARTIAL EPZ MULPLR+$3
MULCND EPZ PARTIAL+$3
It should be stressed that the above routines are for UNSIGNED
mulitiplication only. Signed multiplication is accomplished by first
noting the signs of the multiplier and multiplicand and setting a
sign flag if the sign bits do not equal each other. The absolute
value of the multiplier and multiplicand is then taken, and the
unsigned mulitplication routine is used. After the unsigned mul-
tiplication takes place, the sign flag is tested. If it indicates that
the original sign bits were not equal to one another, the product
must be negated.
13-5
*****************************************************************
As with the unsigned multiply routine, you can check for overflow
by OR'ing PARTIAL with PARTIAL+$1 and checking for zero. A
signed multiply routine is provided in the older Apple monitor at
location $FB60. You should study the technique used in the Apple
13-6
*****************************************************************
DIVISION ALGORITHMS.
13-7
*****************************************************************
;
;
DIVEND EPZ $50
PARTIAL EPZ DIVEND+$2
DIVSOR EPZ PARTIAL+$2
13-8
*****************************************************************
LDA PARTIAL
STA MODULO
LDA PARTIAL+$1
STA MODULO+$1
ETC ...
But there is one little "gotcha" which didn't occur with the
multiply routine. If a division by zero occurs (within the unsigned
multiply routine) $FFFF is returned. The only way (using the
unsigned routine) that $FFFF can be returned is if you divide
$FFFF by one. With the signed routines, however, you get a result
of $FFFF (which is -1 in decimal) by dividing $FFFF by one, one
by $FFFF, or in fact any division where both the positive and
negative versions of a number end up in the divisor and dividend.
Zero division causes the result of $FFFF to be returned. Since
these cases are not all that rare, some steps have to be taken to
correct the possible ambiguity. In the signed division routine
which follows, the overflow flag is set or cleared depending on
whether or not a zero division has occurred. If a division by zero
occurred, the overflow flag will be set. If a division by zero did not
occur, then the overflow flag will be cleared. Your programs can
check the overflow flag upon return from the divsion routine and
then take the appropriate action. You can also use this technique
with the unsigned divsion routine to handle the case of $FFFF
divided by one, if desired.
SDIV:
PHA
LDA DIVEND+$1 ;CHECK SIGN BITS
XOR DIVSOR+$1
13-9
*****************************************************************
AND #$80
STA SIGN
JSR DABS1 ;ABSOLUTE VALUE OF DIVSOR
JSR DABS2 ;ABSOLUTE VALUE OF DIVEND
JSR USDIV ;COMPUTE UNSIGNED DIVISION
LDA DIVEND ;CHECK FOR ZERO DIVIDE
AND DIVEND+$1
CMP #$FF
BEQ OVRFLW
LDA SIGN ;SIGN IF RESULT MUST BE
BPL SDIV1 ;NEGATIVE
JSR DIVNEG
;
SDIV1 CLV ;NO ZERO DIVISION
PLA
RTS
13-10
*****************************************************************
13-11
*****************************************************************
CHAPTER 14
STRING HANDLING
OPERATIONS
STRING HANDLING.
14-1
*****************************************************************
14-2
*****************************************************************
Finally, when the string has been transferred, the length of the
string must be stored in the first location. The routine which does
all of these mystical and magical things follows:
If you read a line of text from the Apple keyboard, using the
monitor GETLNZ routine, you could convert it to a Type 3 string
using the following code sequence:
14-3
*****************************************************************
ETC.
TYPE2 EPZ $0
TYPE3 EPZ TYPE2+$2
;
;
T2TO3:
PHP ;SAVE THE REGISTERS
PHA
TYA
PHA
;
INC TYPE3 ;MOVE PAST THE LENGTH BYTE
BNE T2TO3A
INC TYPE3+$1
;
T2TO3A:
LDY #$0 ;INITIALIZE STRING INDEX
T2TO3B LDA (TYPE2),Y
BPL T2TO3C
STA (TYPE3),Y
INY
BNE T2TO3B ;PREVENT OVERFLOW
DEY ;TRUNCATE TO 255 CHARS
;
T2TO3C ORA #$80 ;STORE LAST CHARACTER
STA (TYPE3),Y
INY ;ADJUST LENGTH
BNE T2TO3D ;TEST FOR OVERFLOW
DEY ;TRUNCATE IF > 255 CHARS
14-4
*****************************************************************
;
T2TO3D LDA TYPE3 ;MOVE TYPE3 POINTER BACK
BNE T2TO3D ;LENGTH BYTE
DEC TYPE3+$1
T2TO3E DEC TYPE3
;
PLA ;RESTORE THE REGISTERS
TAY
PLA
PLP
RTS
STRING ASSIGNMENTS.
*****************************************************************
14-6
*****************************************************************
STRING FUNCTIONS.
then a simple LDA STRING will load the length of the string into
the accumulator.
With the length function out of the way, string output is next
on the list. String output is very easy. The following routine will
output the string stored at location 'STRING:'
Note that the Y-register is loaded with zero, and then the accu-
mulator is loaded from location STRING plus one. This insures
us that the Y-register will be equal to the length of the string when
it is pointing to one character beyond the end of the string, so that
the Y-register will always be less than the length of the string
14-7
*****************************************************************
PRTSTR:
STA ASAVE
STY YSAVE
PLA ;GET RETURN ADDRESS FROM
STA RTNADR ;THE 6502 STACK
PLA
STA RTNADR+$1
;
JSR INCRTN ;INCREMEOT THE RETURN ADDRESS
LDY #$0
LDA (RTNADR),Y ;GET L.O. ADDRESS OF STRING
STA ZPAGE
INY
LDA (RTNADR),Y ;GET H.O.ADDRESS OF STRING
STA ZPAGE+$1
;
JSR INCRTN ;MOVE RTNADR PAST THE ADDRESS
JSR INCRTN ;BYTES
;
;
; AT THIS POINT, ZPAGE POINTS TO THE STRING WHICH
; IS SUPPOSED TO BE OUTPUT
;
DEY ;RESET Y REG TO ZERO
LDA (ZPAGE),Y ;GET THE LENGTH OF THE STRING
STA LENGTH ;AND STORE IT IN "LENGTH"
PRTS1 INY ;MOVE TO THE NEXT CHARACTER
CPY LENGTH ;ARE WE THROUGH YET?
BEQ PRTS2
;
LDA (ZPAGE),Y ;GET THIS CHARACTER
JSR COUT ;AND OUTPUT
JMP PRTS1 ;MOVE TO NEXT CHAR AND REPEAT
;
PRTS2 LDA ASAVE ;RESTORE THE REGISTERS
LDY YSAVE
JMP (RTNADR) ;SIMULATE AN RTS
;
;
;
ASAVE EPZ $0 ;ZERO PAGE WORKSPACE
YSAVE EPZ ASAVE+$1
ZPAGE EPZ YSAVE+$1
RTNADR EPZ ZPAGE+$2
14-8
*****************************************************************
EXAMPLE:
JMP START
STRING STR "HELLO THERE"
;
START JSR PRTSTR
ADR STRING
ETC.
STRING CONCATENATION.
14-9
*****************************************************************
sum of the two source string lengths is less than the maximum
number of characters possible for the destination string, the sum
of the two lengths is stored in the first byte of the destination
string. This will be the length of the new string. Next, the first
string is transferred to the destination string. Finally, the second
source string is transferred to the destination string immediately
after the first string. A short routine which concatenates STR1
and STR2 storing the result at STR3, is:
ETC...
SUBSTRING OPERATIONS.
14-10
*****************************************************************
; SUBSTRING EXAMPLE
;
STR1 EPZ $0
STR2 EPZ STR1+$2
START EPZ STR2+$2
LEN1 EPZ START+$1
MAXSTR EPZ LEN1+$1
INDEX EPZ MAXSTR+$1
LENGTH EPZ INDEX+$1
;
;
SUBSTR:
PHP
PHA
TYA
PHA
;
;
; CHECK TO SEE IF LENGTH OF SUBSTRING IS GREATER
; THAN THE LENGTH OF STR2 (PASSED IN MAXSTR)
LDA MAXSTR
CMP LENGTH
BLT ERROR
;
; CHECK TO SEE IF ENOUGH CHARS IN STR1
;
CLC
LDA INDEX
BEQ ERROR ;INDEX OF ZERO NOT ALLOWED
ADC LENGTH
BCS ERROR ;IF > 255 THEN ALWAYS AN ERROR
14-11
*****************************************************************
LDY #$0
LDA (STR1),Y ;GET LENGTH OF SOURCE STRING
CMP LEN1 ;SEE IF GREATER OR EQUAL
BLT ERROR ;ERROR OTHERWISE
;
; NOW, TRANSFER THE SUBSTRING
;
LDA LENGTH
STA (STR2),Y ;INIT LENGTH
CLC ;SET UP POINTER TO BEGINNING
LDA STR1 ;OF SUBSTRING
ADC INDEX
STA STR1
BCC SUBST1
INC STR1+$1
;
SUBST1 INC STR2 ;INCREMENT PAST LENGTH BYTE
BNE SUBST2
INC STR2+$1
;
SUBST2 CPY LENGTH
BGE SUBST3
LDA (STR1),Y
STA (STR2),Y
INY
JMP SUBST2
;
SUBST3 PLA
TAY
PLA
PLP
RTS
STRING COMPARISONS.
1) Two strings are equal if and only if their lengths are equal
and each character in the first string equals the corre-
sponding character in the second string.
14-12
*****************************************************************
; STRING COMPARE #1
; TEST FOR EQUALITY
;
; THIS ROUTINE COMPUTES THE COMPARISON
; (STR1) = (STR2)
; AND RETURNS TRUE OR FALSE IN THE ACCUMULATOR
STREQU:
PHP ;PRESERVE C & V FLAGS
TYA
PHA ;SAVE THE Y REGISTER
;
LDY #$0
LDA (STR1),Y
CMP (STR2),Y ;COMPARE LENGTHS
BNE NOTEQL ;AND QUIT IF NOT EQUAL
;
; IF LENGTHS ARE EQUAL, SET UP INDICIES
; TO THE BEGINNING OF THE STRINGS
;
STA LENGTH ;SAVE LENGTH OF STRINGS
INC STR1
BNE SEQU1
14-13
*****************************************************************
INC STR1+$1
;
SEQU1 INC STR2
BNE SEQU2
INC STR2+$1
;
SEQU2 LDA (STR1),Y ;PERFORM COMPARISONS
CMP (STR2),Y
BNE SEQU3
INY
CPY LENGTH
BLT SEQU2
;
;
; THE STRINGS ARE EQUAL
;
;
JSR DECSTR ;RESTORE STR1,STR2
;
PLA ;RESTORE Y & PSW REGISTERS
TAY
PLP
LDA #TRUE ;RETURN TRUE
RTS
;
;
; STRINGS ARE NOT EQUAL HERE
;
SEQU3 JSR DECSTR
NOTEQL PLA ;RESTORE Y & PSW
TAY
PLP
LDA #FALSE ;RETURN FALSE IF NOT EQUAL
RTS
;
;
;
;
; DECSTR- RESETS STR POINTERS TO THEIR ORIGINAL
; VALUESVALUES
DECSTR:
s66 LDA STR1 ;RESTORE STRn POINTERS
BNE SEQU4
DEC STR1+$1
SEQU4 DEC STR1
LDA STR2
BNE SEQU5
DEC STR2+$1
SEQU5 DEC STR2
RTS
;
;
;
;
;
; STRING COMPARE #2
; TEST FOR LESS THAN
14-14
*****************************************************************
;
; THIS ROUTINE COMPUTES
; STR1 < STR2
;
; ON RETURN, IF STR1 < STR2 THEN THE ACCUMULATOR IS
; RETURNED WITH TRUE. IF STR1 >= STR2 THEN THE
; ACCUMULATOR IS RETURNED WITH THE VALUE FALSE
;
;
STRLES:
PHP ;PRESERVE C & V FLAGS
TYA ;SAVE Y REGISTER
PHA
;
LDY #$0
LDA (STR2) ,Y ;COMPUTE THE MINIMUM LENGTH
STA MINLEN
CMP (STR1) ,Y
BGE STRLS1
LDA (STR1) ,Y
STA MINLEN
;
STRLS1 INY ;TEST LOOP
LDA (STR1) ,Y
CMP (STR2) ,Y
BGE NOTLES
CPY MINLEN
BLT STRLS1
BEQ STRLS1
;
; ALL CHARACTERS UP TO THE MINIMUM LENGTH ARE EQUAL
; NOW SEE IF THE LENGTH OF STR1 IS LESS THAN THE
; LENGTH OF STR2
;
LDY #$0
LDA (STR1),Y
CMP (STR2),Y
BGE NOTLES
;
; NOW STR1 < STR2
;
PLA ;RESTORE THE Y REGISTER
TAY
PLP ;RESTORE PSW
LDA #TRUE ;TRUE BECAUSE STR1 < STR2
RTS
;
;
NOTLES PLA
TAY
PLP
LDA #FALSE
RTS
;
;
;
; STRING COMPARE #3
; TEST TO SEE IF STR1 > STR2
;
14-15
*****************************************************************
14-16
*****************************************************************
- OR -
- OR -
ETC...
14-17
*****************************************************************
14-18
*****************************************************************
;
; NO MORE ENTRIES, RETURN FALSE AND LEAVE X REGISTER
; POINTING TO THE BEGINNING OF THE TABLE
;
PLA
TAY
PLP
LDA #FALSE
RTS
;
;
;
; SAMPLE TABLE, EACH ENTRY MUST CONTAIN "NUMCHR" NUM
; OF CHARACTERS (IN THIS CASE, THREE)
; OF CHARACTERS (IN THIS CASE, THREE)
;
;
TABLE ASC "ABC"
ASC "DEF"
ASC "GHI"
ASO "JKL"
ASC "MNO"
ASC "PQR"
ASC "STU"
ASC "VWX"
ASC "YZ "
ASC "ETC"
TBLEND EQU *
END
14-19
*****************************************************************
14-20
*****************************************************************
NXT2 INY
NXT1 INY
LDX #$0
CPX TBLENG
BLT LOOP0
;
; END OF TABLE HAS BEEN REACHED
;
PLA
TAX ;LEAVE X REG POINTING TO CHARS
PLA
TAY
PLP
LDA #FALSE ;STRING NOT FOUND
RTS
;
;
; SAMPLE TABLE, EACH ENTRY MUST CONTAIN "NUMCHR" NUM
; OF CHARACTERS (IN THIS CASE, THREE)
;
;
;
TABLE ASC "ABC"
ASC "DEF"
ASC "GHI"
ASC "JKL"
ASC "MNO"
ASC "PQR"
ASC "STU"
ASC "VWX"
ASC "YZ
ASC "ETC"
TBLENG EQU *-TABLE
END
14-21
*****************************************************************
CHAPTER 15
SPECIALIZED I/O
15-1
*****************************************************************
been interfaced to the game I/O connector. In all, the game I/O
connector makes the APPLE II computer one of the most flexible
computers around.
These memory locations all fall within the 128 bytes in the
$C000 to $C07F range. For instance, we've already encountered
the Apple keyboard whose input can be obtained at location
$C000. If bit seven of location $C000 is set, then a key has been
pressed on the Apple keyboard. If bit seven is clear, then no key
has been pressed and the program must wait for a key to be
pressed if the program requires input. In order to clear bit seven
of the keyboard location after the desired data has been retrieved
(so that the next time $C000 is accessed you won't read the same
key code again), location $C010 must be accessed. Accessing
location $C010 clears bit seven of location $C000 so that another
key can be read from the Apple keyboard. The following routine
works fine as a keyboard input routine:
KEYIN, when called, waits until a key is pressed and then returns
with the ASCII code of the key pressed in the accumulator. The
accumulator is stored into location $C010 to clear bit seven of the
keyboard for reasons previously mentioned.
15-2
*****************************************************************
In this routine, bit seven is shifted into the carry and then back
into bit zero of the accumulator. The accumulator is then AND'ed
with $1 so that only bit zero is left in the accumulator. If a key has
been pressed, the result of the AND #$1 is one. If a key has not
been pressed, the result of the AND #$1 is zero.
15-3
*****************************************************************
15-4
*****************************************************************
graphics mode. Location $0051 does just the inverse, it sets the
text mode. The text mode is available in two forms: primary page
and secondary page text. The primary text page resides in mem-
ory from location $400 through location $7FF. The secondary text
page resides in memory from location $800 through location
$BFF. Location $C052 sets the no mix (or full graphics) mode.
Accessing this location produces visible results only if the APPLE
II computer is currently in the graphics mode. In the text mode,
accessing location $C052 produces no visible effect. Location
$C053 is used to set the mixed graphics mode. In this mode, four
lines of text are displayed at the bottom of the screen. Obviously,
this mode is valid only when graphics are in effect. Location
$C054 selects the primary display page. For the text page and
LORES graphics, the memory area which will be utilized is $400
thru $7FF. For HIRES graphics, locations $2000 thru $4000 will
be used. Accessing location $C055 selects the secondary display
page. This is $800 thru $BFF for text and LORES graphics, $4000
through $7FFF for HIRES graphics. Accessing location $C056
sets up the APPLE II computer for LORES GRAPHICS. The
graphics mode must also be set for this to take effect. Accessing
location $C057 sets up the APPLE II computer HIRES graphics.
Once again, the graphics mode must be set (location $C050)
before HIRES graphics will be displayed.
15-5
*****************************************************************
than the audio cassette for mass storage? Well the disk is cer-
tainly much better for mass storage (yes, you can sleep easy on
that tonight), but the audio cassette input allows you to perform
something which the disk could never do. It allows you to input
and digitize speech and other natural sounds. The following rou-
tine can be considered something of a "teaser." If you connect a
crystal microphone (or other high-output microphones) to the cas-
sette input jack and run the following routine -- lo and behold, what
goes into the microphone comes out of the speaker. Try it!
Beyond this basic loop it is possible to get the data from the
cassette input, pack it, and store it into successive memory loca-
tions so that it can be saved to disk and output at a later date.
Several experiments in speech synthesis and speech recognition
can be performed without spending an extra nickel for additional
hardware (except, of course, for a cheap microphone to plug into
the back of the APPLE II computer).
15-6
*****************************************************************
There are two things to keep in mind when using the analog
inputs. First, you cannot read two paddle inputs immediately after
one another. Due to the hardware used, you must delay a little
while before reading another input. The loop:
LDX #$0
LOOP DEX
BNE LOOP
15-7
*****************************************************************
15-8
*****************************************************************
CHAPTER 16
AN INTRODUCTION TO
SWEET-16
SWEET-16
16-1
*****************************************************************
16-2
*****************************************************************
16-3
*****************************************************************
The 16-bit value can be any valid LISA address expression. 'n'
is simply a hex value in the range $0-$F and denotes which reg-
ister is to be loaded with the declared value. Examples of the SET
instruction:
The SET instruction is three bytes long: one byte for the SET
opcode and two bytes for the 16-bit value that is to be loaded into
the specified register. SET RF,<VALUE> is a very special case.
Since RF is the Sweet-16 program counter, loading immediate
data into register $F is the same as performing an absolute jump
instruction. RC and RE must be treated carefully as well since
they are used to hold the Sweet-16 stack pointer and status reg-
ister. If zero is loaded into the specified register, the Sweet-16
zero flag is set; otherwise it is cleared. If minus one ($FFFF) is
loaded into the specified Sweet-16 register, the minus one flag is
set; otherwise the minus one flag is cleared. The Sweet-16 carry
flag is always cleared after a SET instruction is executed.
The next instruction in the Sweet-16 instruction set is the
load register or LDR instruction. This instruction loads the Sweet-
16 accumulator (R0) from the register specified in the operand
field. The term 'load' is somewhat misleading as this instruction
really a register transfer instruction not unlike the 6502 TYA and
TXA instructions. The LDR instruction has the syntax:
LDR Rn
16-4
*****************************************************************
You will note that there is no direct way to transfer the data
from one register to another without going through the Sweet-16
accumulator. For example, to transfer the data from R5 to R6 you
must execute the code sequence:
LDR R5
STO R6
ADD Rn
Where n is a hex value in the range $0-$F. Note that the instruc-
tion 'ADD R0' is very useful; it doubles the value in the Sweet-16
16-5
*****************************************************************
16-6
*****************************************************************
called, the return address is pushed onto the Sweet-16 return
address stack. The stack pointer is RC. Wherever RC happens
to be pointing when the BSB instruction is executed, the return
address will be stored. If you have not initialized the Sweet-16
stack pointer (RC), it could be pointing anywhere in memory,
which means that a BSB instruction could potentially wipe out
valuable program and data storage.
16-7
*****************************************************************
LDR @Rn
Note that the mnemonic is the same as the normal load register
instruction, but that the '@' character appears in the operand field
immediately before the register specifier. This instruction is an 8-
bit load instruction. It loads the low-order bits of the Sweet-16
accumulator from the memory location pointed to by the specified
register. The high-order byte of the Sweet-16 accumulator is cleared.
After the accumulator is loaded with the data from the address
pointed to by Rn, Rn is incremented by one. This causes the
pointer register to point to the next available byte immediately
after the LDR instruction is executed. This type of instruction
(where the register is automatically incremented for you) is called
an "auto-increment" instruction. The LDR indirect instruction is
very useful for memory movements and searches. Consider the
following code:
To load two bytes into the accumulator one would use the
LDD (load double indirect) instruction. It uses the syntax:
LDD @Rn
It loads the low order accumulator byte from the location pointed
at by Rn; then Rn is incremented by one. After the increment is
performed, the high order accumulator byte is loaded indirectly
through the new value in Rn. Once this is accomplished, Rn is
again incremented. The net result is that the Sweet-16 accumu-
lator is loaded indirectly from the locations pointed at by Rn and
Rn+1. Afterwards Rn is incremented twice. The branch condi-
16-8
*****************************************************************
tions will reflect the final accumulator contents and the carry will
be cleared.
STO @Rn
LDR R1
CPR R2 ;DONE YET?
BNC LOOP ;IF NO CARRY (I.E. LESS THAN)
BIZ LOOP ;IF EQUAM
RTN
BRK
END
16-9
*****************************************************************
POP @Rn
PPD @Rn
Double byte stacks may be implemented using the PPD and STD
instructions. The POP, STP, and PPD instructions are all one byte
long. The carry is always cleared after one of these operations is
performed. POP always results in a positive value which is never
minus one. PPD and STP affect the status bits depending upon
the final accumulator contents.
16-10
*****************************************************************
16-11
*****************************************************************
CHAPTER 17
DEBUGGING 6502
MACHINE LANGUAGE
PROGRAMS.
GENERAL.
17-1
*****************************************************************
GO COMMAND (G).
17-2
*****************************************************************
17-3
*****************************************************************
The control-E command in the Apple monitor allows you to
display the contents of the 6502 registers. If you type control-E
followed by return the APPLE II computer will display something
like:
This tells you that when the GO command is issued, the 6502
registers will contain their respective displayed value. Great, so
we know how to find out what data will be passed to a routine.
But how can we change it? As it turns out, whenever you type
control-E followed by return the monitor is set up so that if you
type a colon (:) followed by some byte data, you can modify the
registers.
EXAMPLE:
*control-E
*:C1
*control-E
EXAMPLE:
*control-E
17-4
*****************************************************************
*:C1 FF D8 B0 FF
*control-E
*control-E
*:C1
*FDF0G
A
*control-E
*:C2
*FDF0G
B
*control-E etc.
17-5
*****************************************************************
EXAMPLE:
*F0:00 80 C0
*800G
This example assumes that there is some subroutine at location
$800 which uses the data in locations $F0, $F1, and $F2. If you
have a subroutine that needs data passed to it in memory, you
can handle its testing in a similar manner.
*1000:20 00 09 C1 C2 C3 00 60
*10000
ABC
*
If you've been learning your machine code all along (or if you're
like me, you cheat and look up the opcodes on a 6502 reference
card), you'll notice that the above sequence represents the code:
JSR $900
ASC "ABC"
HEX 00
RTS
By typing 1000G and executing this short routine, you can test
the PRINT routine to see if it works properly. For additional infor-
mation on modifying memory locations, consult Chapter 3 of the
new Apple Reference Guide (the 'White' book).
17-6
*****************************************************************
The NOP's main use in the 6502 instruction set lies in timing
delays and its ability to replace existing instructions without alter-
ing any registers or memory locations. The opcode for the NOP
instruction is $EA; memorize it! When debugging programs, you
will often need to replace an instruction with one or more NOP's.
The only easy way to enter a NOP into your instruction stream is
17-7
*****************************************************************
As you may recall, the 6502 BRK instruction stops the pro-
gram and prints out the contents of the 6502 registers. This fea-
ture will prove to be extremely useful for debugging programs. By
replacing an instruction within your program with the BRK instruc-
tion, you can stop the program before (or after) some critical
section of code and examine the registers or some specific mem-
ory locations. The opcode for the BRK instruction is easy to
remember: it's zero.
17-8
*****************************************************************
17-9
*****************************************************************
The 'C' option lets you return to the executing program (or
TRACE/65 command level) without executing any of the param-
eter mode commands. This is useful in the event 'P' is accidentally
pressed.
The 'D' option is used to quit the trace mode. This option
lets you terminate program interpretation after a desired section
of code is checked out.
PC :$DA, $DB
ACC :$E5
XREG:$E6
YREG:$E7
PSW :$E8
SP :$E9
Incidently, these are the only zero page locations in the range
$D6-$FF you or your program should modify. Should any of these
memory locations be modified, unpredictable things may happen.
START:
LDX #$0
LOOP LDA MSG,X
BEQ QUIT
JSR $FDED
17-10
*****************************************************************
DEX
BNE LOOP
;
MSG ASC "THIS IS A TEST"
HEX 00
;
QUIT BRK
END
To begin the trace mode, type "T." TRACE/65 will prompt you
for a beginning address. Once this is entered, the trace mode
begins. Since a breakpoint was set at location $FDED, the trace
will quickly stop with the message 'BREAK POINT ENCOUN-
TERED AT LOCATION $nnnn. Notice right below the last instruc-
tion displayed, that the accumulator's contents are displayed.
Currently the accumulator will contain $D4 (T), which is the first
character in our string. Fine, things are working out okay so far.
To continue execution (without executing the COUT routine), the
parameter mode must be entered (by typing "P"). Now type
$DA:58FF. This will point the program counter at a RTS instruction
which will cause an immediate return to your program. Finally,
type "C" to continue the execution of your program. Another batch
of instructions will be executed, and once again the program will
encounter a breakpoint. This time, however, there is probably
garbage in the accumulator, and the X-index register will contain
$FF. By tracing back a few instructions on the screen, you will
17-11
*****************************************************************
17-12
*****************************************************************
APPENDIX A
APPLE II COMPUTER
TABLES, CHARTS,
AND GRAPHS
omitted
*****************************************************************
*****************************************************************
OR Function 9-3
Outputting Byte Data as a Decimal
Value 12-2
Outputting Signed 16-Bit Integers 12-6
Outputting 16-Bit Unsigned
Integers 12-4