6502 Assembly Language Subroutines PDF
6502 Assembly Language Subroutines PDF
Assembly Language
Subroutines
Lance A. Leventhal
Winthrop Saville
OSBORNE/McGraw-Hill
Berkeley, California
Disclaimer of Warranties
and Limitation of Liabilities
The authors have taken due care in preparing this book and the programs
in it, including research, development, and testing to ascertain their
effectiveness. The authors and the publishers make no expressed or
implied warranty of any kind with regard to these programs nor the sup
plementary documentation in this book. In no event shall the authors or
the publishers be liable for incidental or consequential damages in con
nection with or arising out of the furnishing, performance, or use of any
of these programs.
Published by
Osborne/ McGraw-Hill
2600 Tenth St.
Berkeley, California 94710
U.S.A.
For information on translations and book distributors outside of the U.S.A., please write OSBORNE/
McGraw-Hill at the above address.
Copyright© 1982 by McGraw-Hill, Inc. All rights reserved. Printed in the United States of America.
Except as permitted under the Copyright Act of 1976, no part of this publication may be reproduced
or distributed in any form or by any means, or stored in a data base or retrieval system, without the
prior written permission of the publisher, with the exception that the program listings may be
entered, stored, and executed in a computer system, but they may not be reproduced for publication.
34567890 DODO 876543
ISBN 0-931988-59-4
Cover art by Jean Frega.
Preface v
6 Arithmetic 230
7 Bit Manipulation and Shifts 306
8 String Manipulation 345
9 Array Operations 382
10 Input/Output 418
11 Interrupts 464
A 6502 Instruction Set Summary 505
B Programming Reference for the 6522 Versatile
Interface Adapter (VIA) 510
Index 543
Hi
Preface
This book is intended to serve as a source and a reference for the assembly
language programmer. It contains an overview of assembly language program
ming for a particular microprocessor and a collection of useful routines. In writing
the routines, we have used a standard format, documentation package, and
parameter passing techniques. We have followed the rules of the original
manufacturer's assembler and have described the purpose, procedure, param
eters, results, execution time, and memory usage of each routine.
This overview of assembly language programming provides a summary for
those who do not have the time or need for a complete textbook such as is pro
vided already in the Assembly Language Programming series. Chapter 1 contains
an introduction to assembly language programming for the particular processor
and a brief summary of the major features that differentiate this processor from
other microprocessors and minicomputers. Chapter 2 describes how to imple
ment instructions and addressing modes that are not explicitly available. Chapter
3 discusses common errors that the programmer is likely to encounter.
The collection of routines emphasizes common tasks that occur in many
applications such as code conversion, array manipulation, arithmetic, bit
manipulation, shifting functions, string manipulation, summation, sorting, and
searching. We have also provided examples of I/O routines, interrupt service
routines, and initialization routines for common family chips such as parallel
interfaces, serial interfaces, and timers. You should be able to use these routines
as subroutines in actual applications and as guidelines for more complex pro
grams.
We have aimed this book at the person who wants to use assembly language
immediately, rather than just learn about it. The reader could be
This book can also serve as supplementary material for students of the Assem
bly Language Programming series.
This book should save the reader time and effort. There is no need to write,
debug, test, or optimize standard routines, nor should the reader have to search
through material with which he or she is thoroughly familiar. The reader should
• be able to obtain the specific information, routine, or technique that he or she
needs with a minimum amount of effort. We have organized and indexed this
book for rapid use and reference.
Obviously, a book with such an aim demands response from its readers. We
have, of course, tested all the programs thoroughly and documented them
carefully. If you find any errors, please inform the publisher. If you have sugges
tions for additional topics, routines, programming hints, index entries, and so
forth, please tell us about them. We have drawn on our programming experience
to develop this book, but we need your help to improve it. We would greatly
appreciate your comments, criticisms, and suggestions.
NOMENCLATURE
We have used the following nomenclature in this book to describe the
architecture of the 6502 processor, to specify operands, and to represent general
values of numbers and addresses.
6502 Architecture
Byte-length registers include
A (accumulator)
PREFACE VH
F (flags, same as P)
P (status register)
S or SP (stack pointer)
X (index register X)
Y (index register Y)
Of these, the general purpose user registers are A, X, and Y. The stack pointer
always contains the address of the next available stack location on page 1 of
memory (addresses 010016 through 01FF16). The P (status) or F (flag) register
consists of a set of bits with independent functions and meanings, organized as
shown in the following diagram:
7 6 5 4 3 2 10 -* Bit Number
-Carry
-Zero
-Interrupt disable
-Decimal mode
-Break command
-Overflow
-Negative (Sign)
PC (program counter)
Note: Pairs of memory locations on page 0 may also be used as word-length
registers to hold indirect addresses. The lower address holds the less significant
byte and the higher address holds the more significant byte. Since the 6502 pro
vides automatic wraparound, addresses 00FF16 and 0000l6 form a rarely used pair.
Flags include
Break (B)
Carry (C)
Decimal Mode (D)
Interrupt Disable (I)
Negative or Sign (N)
Overflow (V)
Zero (Z)
These flags are arranged in the P or F register as shown previously.
6502 ASSEMBLY LANGUAGE SUBROUTINES
6502 Assembler
Delimiters include
space After a label or an operation code
Pseudo-Operations include
.BLOCK Reserve bytes of memory; reserve the specified number of bytes of
memory for temporary storage
.BYTE Form byte-length data; place the specified 8-bit data in the next
available memory locations
.DBYTE Form double-byte (word) length data with more significant byte
first; place the specified 16-bit data in the next available memory
locations with more significant byte first
.END End of program
.EQU Equate; define the attached label
.TEXT Form string of ASCII characters; place the specified ASCII charac
ters in the next available memory locations
.WORD Form double-byte (word) length data with less significant byte first;
place the specified 16-bit data in the next available memory loca
tions with less significant byte first
*= Set origin; assign the object code generated from the subsequent as
sembly language statements to memory addresses starting with the
one specified
= Equate; define the attached label
Designations include
Number systems:
$ (prefix) or H (suffix) Hexadecimal
@ (prefix) or Q (suffix) Octal
% (prefix) or B (suffix) Binary
The default mode is decimal.
Others:
' (in front of character) ASCII
* Current value of location
(program) counter
PREFACE IX
General Nomenclature
ADDR a 16-bit address in data memory
This chapter describes general methods for writing assembly language pro
grams for the 6502 and related microprocessors. It presents techniques for per
forming the following operations:
1
6502 ASSEMBLY LANGUAGE SUBROUTINES
and consumer products. Microcomputer users will make use of these operations
in writing I/O drivers, utility programs, diagnostics, and systems software, and in
understanding, debugging, or improving programs written in high-level
languages. This chapter provides a brief guide to 6502 assembly language pro
gramming for those who have an immediate application in mind.
For those who are familiar with assembly language programming on other pro
cessors, we provide here a brief review of the peculiarities of the 6502. Being
aware of these unusual features can save you a great deal of time and trouble.
2. The only Addition and Subtraction instructions are ADC (Add with Carry)
and SBC (Subtract with Carry). If you wish to exclude the Carry flag, you must
clear it before addition or set it before subtraction. That is, you can simulate a
normal Add instruction with
CLC
ADC MEMORY
3. There are no 16-bit registers and no operations that act on 16-bit quantities.
The lack of 16-bit registers is commonly overcome by using pointers stored on
page 0 and the indirect indexed (postindexed) addressing mode. However, both
initializing and changing those pointers require sequences of 8-bit operations.
4. There is no true indirect addressing except with JMP. For many other
instructions, however, you can simulate indirect addressing by clearing index
register Y and using indirect indexed addressing, or by clearing index register X
and using indexed indirect addressing. Both of these modes are limited to indirect
addresses stored on page 0.
5. The stack is always on page 1 of memory. The stack pointer contains the
less significant byte of the next empty address. Thus, the stack is limited to 256
bytes of memory.
CHAPTER 1: GENERAL PROGRAMMING METHODS
6. The JSR (Jump to Subroutine) instruction saves the address of its own
third byte in the stack, that is, JSR saves the return address minus 1. RTS
(Return from Subroutine) loads the program counter from the top of the stack
and then adds 1 to it. You must remember this offset of 1 in debugging and using
JSR or RTS for purposes other than ordinary calls and returns.
7. The Decimal Mode (D) flag is used to perform decimal arithmetic. When
this flag is set, all additions and subtractions produce decimal results. Increments
and decrements, however, produce binary results regardless of the mode. The
problem with this approach is that you may not be sure of the initial or current
state of the D flag (the processor does not initialize it on Reset). A simple way to
avoid problems in programs that use Addition or Subtraction instructions is to
save the original D flag in the stack, assign D the appropriate value, and restore
the original value before exiting. Interrupt service routines, in particular, should
always either set or clear D before executing any addition or subtraction instruc
tions. The PHP (Store Status Register in Stack) and PLP (Load Status Register
from Stack) instructions can be used to save and restore the D flag, if necessary.
The overall system startup routine must initialize D (usually to 0, indicating bin
ary mode, with CLD). Most 6502-based operating systems assume the binary
mode as a default and always return to that mode as soon as possible.
A minor quirk of the 6502's decimal mode is that the Zero and Negative flags
are no longer universally valid. These flags reflect only the binary result, not the
decimal result; only the Carry flag always reflects the decimal result. Thus, for
example, subtracting 8016 from 5016 in the decimal mode sets the Negative flag
(since the binary result is D016), even though the decimal result (7016) has a most
significant bit of 0. Similarly, adding 50l6 and 5016 in the decimal mode clears the
Zero flag (since the binary result is A016), even though the decimal result is zero.
Note that adding 5016 and 5016 in the decimal mode does set the Carry. Thus when
working in the decimal mode, the programmer should use only branches that
depend on the Carry flag or operations that do not depend on the mode at all
(such as subtractions or comparisons followed by branches on the Zero flag).
8. Ordinary Load (or Pull from the Stack) and Transfer instructions (except
TXS) affect the Negative (Sign) and Zero flags. This is not the case with the 8080,
8085, or Z-80 microprocessors. Storing data in memory does not affect any flags.
9. INC and DEC cannot be applied to the accumulator. To increment A, use
CLC
ADC #1 ;INCREMENT ACCUMULATOR BY 1
To decrement A, use
SEC
SBC #1 ;DECREMENT ACCUMULATOR BY 1
6502 ASSEMBLY LANGUAGE SUBROUTINES
10. The index registers are only 8 bits long. This creates obvious problems in
handling arrays or areas of memory that are longer than 256 bytes. To overcome
this, use the indirect indexed (postindexed) addressing mode. This mode allows
you to store the starting address of the array in two memory locations on page 0.
Whenever the program completes a 256-byte section, it must add 1 to the more
significant byte of the indirect address before proceeding to the next section. The
processor knows that it has completed a section when index register Y returns to
0. A typical sequence is
INY ;PROCEED TO NEXT BYTE
BNE LOOP ;UNLESS A PAGE IS DONE
INC INDR+1 ;IF ONE IS, GO ON TO THE NEXT PAGE
Memory location INDR+1 (on page 0) contains the most significant byte of the
indirect address.
COUNTL contains the less significant byte of a 16-bit counter and COUNTH the
more significant byte. Note that we check the Zero flag rather than the Carry flag
since, as on most computers, Increment and Decrement instructions do not
affect Carry.
12. The BIT instruction (logical AND with no result saved) has several
unusual features. In the first place, it allows only direct addressing (absolute and
zero page). If you want to test bit 3 of memory location ADDR, you must use the
sequence
lda #%ooooiooo
BIT ADDR
BIT also loads the Negative and Overflow flags with the contents of bits 7 and 6 of
the memory location, respectively, regardless of the value in the accumulator.
Thus, you can perform the following operations without loading the accumulator
at all. Branch to DEST if bit 7 of ADDR is 1
BIT ADDR
BM1 DEST
Of course, you should document the special use of the Overflow flag for later
reference.
CHAPTER 1: GENERAL PROGRAMMING METHODS
13. The processor lacks some common instructions that are available on the
6800, 6809, and similar processors. Most of the missing instructions are easy to
simulate, although the documentation can become awkward. In particular, we
should mention Clear (use load immediate with 0 instead), Complement (use
logical EXCLUSIVE OR with the all Is byte instead), and the previously men
tioned Add (without carry) and Subtract (without borrow). There is also no direct
way to load or store the stack pointer (this can be done through index register X),
load or store the status register (this can be done through the stack), or perform
operations between registers (one must be stored in memory). Other missing
instructions include Unconditional Relative Branch (use jump or assign a value
to a flag and branch on it having that value), Increment and Decrement
Accumulator (use the Addition and Subtraction instructions), Arithmetic Shift
(copy bit 7 into Carry and rotate), and Test zero or minus (use a comparison with
0 or an increment, decrement sequence). Weller1 describes the definition of
macros to replace the missing instructions.
• The accumulator is the center of data processing and is used as a source and
destination by most arithmetic, logical, and other data processing instructions.
• Index register X is the primary index register for non-indirect uses. It is the
only register that normally has a zero page indexed mode (except for the LDX
STX instructions), and it is the only register that can be used for indexing with
single-operand instructions such as shifts, increment, and decrement. It is also
the only register that can be used for preindexing, although that mode is not com
mon. Finally, it is the only register that can be used to load or store the stack
pointer.
• Index register Y is the primary index register for indirect uses, since it is the
only register that can be used for postindexing.
• Memory locations on page 0 are the only locations that can be accessed with
the zero page (direct), zero page indexed, preindexed, and postindexed address
ing modes.
Tables 1-1 through 1-7 contain lists of instructions having particular features.
Table 1-1 lists instructions that apply only to particular registers and Table 1-2
lists instructions that can be applied directly to memory locations. Tables 1-3
through 1-7 list instructions that allow particular addressing modes: zero page
(Table 1-3), absolute (Table 1-4), zero page indexed (Table 1-5), absolute
indexed (Table 1-6), and preindexing and postindexing (Table 1-7).
• Accumulator. Source and destination for all arithmetic and logical instruc
tions except CPX, CPY, DEC, and INC. Only register that can be shifted with a
single instruction. Only register that can be loaded or stored using preindexed or
postindexed addressing.
• Memory locations on page 0. Only memory locations that can hold indirect
addresses for use in postindexing or preindexing. Only memory locations that can
be accessed using zero page or zero page indexed addressing.
• Status register. Can only be stored in the stack using PHP or loaded from
the stack using PLP.
CHAPTER 1: GENERAL PROGRAMMING METHODS
Register Instructions
A ADC, AND, ASL, BIT, CMP, EOR, LDA, LSR, ORA, PHA,
PLA, ROL, ROR, SBC, STA, TAX, TAY, TXA, TYA
P (processor status) PHP, PLP (CLC, CLD, CLV, SEC, and SED affect
particular flags)
S (stack pointer) JSR, PHA, PHP, PLA, PLP, RTS, TSX, TXS
X CPX, DEX, INX, LDX, STX, TAX, TSX, TXA, TXS
Y CPY, DEY, INY, LDY, STY, TAY, TYA
Instruction Function
Instruction Function
Instruction Function
Instruction Function
Instruction Function
Instruction Function
• Almost all data processing involves the accumulator, since it provides one
operand for arithmetic and logical instructions and the destination for the result.
• Only a limited number of instructions operate directly on the index registers
or on memory locations. An index register can be incremented by 1, decre
mented by 1, or compared to a constant or to the contents of an absolute address.
The data in a memory location can be incremented by 1, decremented by 1,
shifted left or right, or rotated left or right.
Register Transfers
The 6502 microprocessor offers many methods for loading registers from
memory. The following addressing modes are available: zero page (direct),
absolute (direct), immediate zero page indexed, absolute indexed, postindexed,
and preindexed. Osborne3 describes all these modes in Chapter 6 of An Introduc
tion to Microcomputers: Volume 1 — Basic Concepts.
The accumulator, index register X, and index register Y can be loaded from
memory using direct addressing. A special zero page mode loads registers from
CHAPTER 1: GENERAL PROGRAMMING METHODS 1 1
addresses on page 0 more rapidly than from addresses on other pages. Ter
minology for 6502 refers to zero page direct addressing as zero page addressing and
to the more general direct addressing as absolute addressing.
Examples
1. LDA $40
This instruction loads the accumulator from memory location 004016. The
special zero page addressing mode requires less time and memory than the more
general absolute (direct) addressing.
2. LDX $C000
This instruction loads index register X from memory location C00016. It uses
absolute (direct) addressing.
This method can be used to load the accumulator, index register X, or index
register Y with a specific value.
Examples
1. LDY #6
This instruction loads index register Y with the number 6. The 6 is an 8-bit
data item, not a 16-bit address; do not confuse the number 6 with the address
0006l6.
2. LDA #$E3
This instruction loads the accumulator with the number E316.
The instructions LDA, LDX, and LDY can be used in the indexed mode. The
limitations are that index register X cannot be loaded using X as an index;
similarly, index register Y cannot be loaded using Y as an index. As with direct
addressing, a special zero page mode is provided. Note, however, that the
accumulator cannot be loaded in the zero page mode using Y as an index.
Examples
1. LDA $0340,X
This instruction loads the accumulator from the address obtained by indexing
with index register X from the base address 034016; that is, the effective address is
034016+(X). This is the typical indexing described in An Introduction to
Microcomputers: Volume 1 — Basic Concepts*
12 6502 ASSEMBLY LANGUAGE SUBROUTINES
2. LDX $40,Y
This instruction loads index register X from the address obtained by indexing
with register Y from the base address 004016. Here the special zero page indexed
mode saves time and memory.
The instruction LDA can be used in the postindexed mode, in which the base
address is taken from two memory locations on page 0. Otherwise, this mode is
the same as regular indexing.
Example
LDA ($40), Y
This instruction loads the accumulator from the address obtained by indexing
with index register Y from the base address in memory locations 004016 and
004116. This mode is restricted to page 0 and index register Y. It also assumes that
the indirect address is stored with its less significant byte first (at the lower
address) in the usual 6502 manner.
The instruction LDA can be used in the preindexed mode, in which the
indexed address is itself used indirectly. This mode is restricted to page 0 and
index register X. Note that it also assumes the existence of a table of 2-byte
indirect addresses, so that only even values in X make sense.
Example
LDA ($40,X)
This instruction loads the accumulator from the indirect address obtained by
indexing with register X from the base address 004016. The indirect address is in
the two bytes of memory starting at 004016+ (X). This mode is uncommon; one
of its uses is to select from a table of device addresses for input/output.
The instruction PLA loads the accumulator from the top of the stack and
subtracts 1 from the stack pointer. The instruction PLP is similar, except that it
loads the status (P) register. This is the only way to load the status register with a
specific value. The index registers cannot be loaded directly from the stack, but
CHAPTER 1: GENERAL PROGRAMMING METHODS 1 3
they can be loaded via the accumulator. The required sequences are
(for index register X)
PLA ;TOP OF STACK TO A
TAX ;AND ON TO X
• It is always located on page 1 of memory. The stack pointer contains only the
less significant byte of the next available address.
• Data is stored in the stack using postdecrementing — the instructions decre
ment the stack pointer by 1 after storing each byte. Data is loaded from the stack
using preincrementing — the instructions increment the stack pointer by 1 before
loading each byte.
STORING REGISTERS
IN MEMORY
The same approaches that we used to load registers from memory can also be
used to store registers in memory. The only differences between loading and stor
ing registers are
• STX and STY allow only zero page indexed addressing. Neither allows
absolute indexed addressing.
• As you might expect, the order of operations in storing index registers in the
stack is the opposite of that used in loading them from the stack. The sequences
are
Examples
1. STA $50
This instruction stores the accumulator in memory location 005016. The special
zero page mode is both shorter and faster than the absolute mode, since the more
significant byte of the address is assumed to be 0.
2. STX $17E8
This instruction stores index register X in memory location 17E816. It uses the
absolute addressing mode with a full 16-bit address.
3. STA $A000,Y
This instruction stores the accumulator in the effective address obtained by
adding index register Y to the base address A00016. The effective address is
A00016+(Y).
4. STA ($50) ,Y
This instruction stores the accumulator in the effective address obtained by
adding index register Y to the base address in memory locations 005016 and
005116. The instruction obtains the base address indirectly.
5. STA ($43,X)
This instruction stores the accumulator in the effective address obtained
indirectly by adding index register X to the base 004316. The indirect address is ifi
the two bytes of memory starting at 004316+ (X).
The normal way to initialize RAM locations is through the accumulator, one
byte at a time. The programmer can also use index registers X and Y for this pur
pose.
Examples
1. Store an 8-bit item (VALUE) in address ADDR.
LDA #VALUE ;GET THE VALUE
STA ADDR ;INITIALIZE LOCATION ADDR
We could use either LDX, STX or LDY, STY instead of the LDA, STA
sequence. Note that the 6502 treats all values the same; there is no special
CLEAR instruction for generating 0s.
CHAPTER 1: GENERAL PROGRAMMING METHODS 1 5
This method allows us to initialize indirect addresses on page 0 for later use with
postindexing &nd preindexing.
Most arithmetic and logical operations (addition, subtraction, AND, OR, and
EXCLUSIVE OR) can be performed only between the accumulator and an 8-bit
byte in memory. The result replaces the operand in the accumulator. Arithmetic
and logical operations may use immediate, zero page (direct), absolute (direct),
indexed, zero page indexed, indexed indirect, or indirect indexed addressing.
Examples
1. Add memory location 004016 to the accumulator with carry.
ADC $40
This instruction adds the contents of memory location 004016 and the contents of
the Carry flag to the accumulator.
AND $B470
Note the following special features of the 6502's arithmetic and logical instruc
tions:
• The only addition instruction is ADC (Add with Carry). To exclude the
Caffy, you must clear it explicitly using the sequence
CLC ;MAKE CARRY ZERO
ADC $40 ;ADD WITHOUT CARRY
1 6 6502 ASSEMBLY LANGUAGE SUBROUTINES
where M is the contents of the effective address. To exclude the Carry, you must
set it explicitly using the sequence
Note that you must set the Carry flag before a subtraction, but clear it before an
addition.
• The BIT instruction performs a logical AND but does not return a result to
the accumulator. It affects only the flags. You should note that this instruction
allows only direct addressing (zero page or absolute); it does not allow immediate
or indexed addressing. More complex operations require several instructions;
typical examples are the following:
Note that we must load the first operand into the accumulator and clear the Carry
before adding the second operand.
CHAPTER 1: GENERAL PROGRAMMING METHODS 1 7
BIT MANIPULATION
The programmer can set, clear, complement, or test bits by means of logical
operations with appropriate masks. Shift instructions can rotate or shift the
accumulator or a memory location. Chapter 7 contains additional examples of bit
manipulation.
You may operate on individual bits in the accumulator as follows:
4. Test bit 5 of the accumulator. Clear the Zero flag if bit 5 is a logic 1 and set
the Zero flag if bit 5 is a logic 0.
You can change more than one bit at a time by changing the masks.
5. Set bits 4 and 5 of the accumulator.
LDA $40
ORA $%00010000 ;SET BIT 4 BY ORING WITH 1
STA $40
LDA $17E0
AND #%11111101 ;CLEAR BIT 1 BY ANDING WITH 0
STA $17E0
• Left shifts set the Carry to the value that was in bit position 7 and the Nega
tive flag to the value that was in bit position 6.
• Right shifts set the Carry to the value that was in bit position 0.
• Rotates preserve all the bits, whereas LSR and ASL destroy the old Carry
flag.
• Rotates allow you to move serial data between memory or the accumulator
and the Carry flag. This is useful in performing serial I/O and in handling single
bits of information such as Boolean indicators or parity.
Multibit shifts simply require the appropriate number of single-bit instruc
tions.
Examples
1. Rotate accumulator right three positions.
ROR A
ROR A
ROR A
CHAPTER 1: GENERAL PROGRAMMING METHODS 1 9
B7|B6 B5 B4 B3 B2 B,|B0|
b6|b5 B4 B3 B2 B,|B0|0
[c] |B7|B6|B5lB4|B3lB2JB1|B0|
After ROR (Rotate Right)
Carry Data
The second approach is shorter (10 bytes rather than 12) and faster (16 clock
cycles rather than 24), but it destroys the previous contents of the accumulator.
You can implement arithmetic shifts by using the Carry flag to preserve the
current value of bit 7. Shifting right arithmetically is called sign extension, since it
copies the sign bit to the right. A shift that operates in this manner preserves the
sign of a two's complement number and can therefore be used to divide or nor
malize signed numbers.
Examples
1. Shift the accumulator right 1 bit arithmetically, preserving the sign (most
significant) bit.
TAX ;SAVE THE ACCUMULATOR
ASL A ;MOVE BIT 7 TO CARRY
TXA ;RESTORE THE ACCUMULATOR
ROR A ;SHIFT THE ACCUMULATOR, COPYING BIT 7
When the processor performs ROR A, it moves the Carry (the old bit 7) to bit 7
and bit 7 to bit 6, thus preserving the sign of the original number.
2. Shift the accumulator left 1 bit arithmetically, preserving the sign (most sig
nificant) bit.
ASL A ;SHIFT A, MOVING BIT 7 TO CARRY
ROL A ;SAVE BIT 7 IN POSITION 0
TAX
LSR A ;CHANGE CARRY TO OLD BIT 7
TXA
ROR A ;SHIFT THE ACCUMULATOR, PRESERVING BIT 7
or
MAKING DECISIONS
The first type of decision allows the processor to sense the value of a flag,
switch, status line, or other binary (ON/OFF) input. The second type of decision
allows the processor to determine whether an input or a result has a specific value
(e.g., an input is a specific character or terminator or a result is 0). The third type
of decision allows the processor to determine whether a value is above or below a
numerical threshold (e.g., a value is valid or invalid or is above or below a warn
ing level or set point). Assuming that the primary value is in the accumulator and
the secondary value (if needed) is in address ADDR, the procedures are as
follows.
The Zero flag is set to 1 if and only if bit 5 of the accumulator is 0. Note the inver
sion here.
If we assume that the data is in address ADDR, we can use the BIT instruction
to produce an equivalent effect. To branch to DEST if bit 5 of ADDR is 1, we can
use either
LDA ADDR
AND #%00100000
BNE DEST
or
LDA #%00100000
BIT ADDR
BNE DEST
We must reverse the order of the operations, since BIT does not allow immediate
addressing. It does, however, leave the accumulator unchanged for later use.
22 6502 ASSEMBLY LANGUAGE SUBROUTINES
There are special short procedures for examining bit positions 0, 6, or 7. Bit 7 is
available readily as the Negative flag after a Load or Transfer instruction; bit 0 can
be moved to the Carry with LSR A or ROR A; bit 6 can be moved to the Negative
flag with ASL A or ROL A.
Note that LDA affects the Zero and Negative flags; so do transfer instructions
such as TAX, TYA, TSX (but not TXS), and PLA. Store instructions (including
PHA) do not affect any flags.
4. Branch to DEST if bit 6 of the accumulator is 0.
ASL A ;MOVE BIT 6 TO BIT 7
BPL DEST
The BIT instruction has a special feature that allows one to readily test bit 6 or
bit 7 of a memory location. When the processor executes BIT, it sets the Negative
flag to the value of bit 7 of the addressed memory location and the Overflow flag
to the value of bit 6, regardless of the contents of the accumulator.
This sequence requires careful documentation, since the Overflow flag is being
used in a special way. Here again, the contents of the accumulator do not change
or affect the sequence at all.
instruction (CMP) is more useful than the Subtract instruction (SBC) because
Compare does not change the accumulator or involve the Carry.
Examples
1. Branch to DEST if the accumulator contains the number VALUE.
CMP #VALUE ;IS DATA = VALUE?
BEQ DEST ;YES, BRANCH
We could also use index register X with CPX or index register Y with CPY.
2. Branch to DEST if the contents of the accumulator are not equal to the con
tents of memory location ADDR.
CMP ADDR ;IS DATA = VALUE IN MEMORY?
BNE DEST ;NO, BRANCH
INC does not affect the Carry flag, but it does affect the Zero flag. Note that you
cannot increment or decrement the accumulator with INC or DEC.
• Determine if the contents of the accumulator are greater than or less than
some other value by subtraction. If, as is typical, the numbers are unsigned, the
Carry flag indicates which one is larger. Note that the 6502's Carry flag is a nega
tive borrow after comparisons or subtractions, unlike the true borrow produced
by such processors as the 8080, Z-80, and 6800. In general,
24 6502 ASSEMBLY LANGUAGE SUBROUTINES
• Carry = 1 if the contents of the accumulator are greater than or equal to the
value subtracted from it. Carry = 1 if the subtraction does not require (generate)
a borrow.
• Carry = 0 if the value subtracted is larger than the contents of the accumula
tor. That is, Carry = 0 if the subtraction does require a borrow.
Note that the Carry is the inverse of a normal borrow. If the two operands are
equal, the Carry is set to 1, just as if the accumulator were larger. If, however, you
want equal values to affect the Carry as if the other value were larger, all that you
must do is reverse the identities of the operands, that is, you must subtract in
reverse, saving the accumulator in memory and loading it with the other value
instead.
Examples
1. Branch to DEST if the contents of the accumulator are greater than or equal
to the number VALUE.
CMP RVALUE ;IS DATA ABOVE VALUE?
BCS DEST ;YESf BRANCH
The Carry is set to 1 if the unsigned subtraction does not require a borrow.
2. Branch to DEST if the contents of memory address OPER1 are less than the
contents of memory address OPER2.
LDA OPERl ;GET FIRST OPERAND
CMP 0PER2 ;IS IT LESS THAN SECOND OPERAND?
BCC DEST ;YESf BRANCH
3. Branch to DEST if the contents of memory address OPERl are less than or
equal to the contents of memory address OPER2.
LDA 0PER2 ;GET SECOND OPERAND
CMP OPERl ;IS IT GREATER THAN OR EQUAL TO FIRST?
BCS DEST ;YES, BRANCH
Since neither of these is what we want, we must handle the operands in the
opposite order.
If the values are signed, we must allow for the possible occurrence of two's
complement overflow. This is the situation in which the difference between the
numbers cannot be contained in seven bits and, therefore, changes the sign of the
result. For example, if one number is +7 and the other is —125, the difference is
CHAPTER 1: GENERAL PROGRAMMING METHODS 25
-132, which is beyond the capacity of eight bits (it is less than -128, the most
negative number that can be contained in eight bits).
Thus, in the case of signed numbers, we must allow for the following two
possibilities:
• The result has the sign (positive or negative, as shown by the Negative flag)
that we want, and the Overflow flag indicates that the sign is correct.
• The result does not have the sign that we want, but the Overflow flag indi
cates that two's complement overflow has changed the real sign.
We have to look for both a true positive (the sign we want, unaffected by over
flow) or a false negative (the opposite of the sign we want, but inverted by two's
complement overflow).
Examples
1. Branch to DEST if the contents of the accumulator (a signed number) are
greater than or equal to the number VALUE.
Note that we must set the Carry and use SBC, because CMP does not affect the
Overflow flag.
Tables 1-8 and 1-9 summarize the common instruction sequences used to
make decisions with the 6502 microprocessor. Table 1-8 lists the sequences that
depend only on the value in the accumulator; Table 1-9 lists the sequences that
depend on numerical comparisons between the contents of the accumulator and a
specific value or the contents of a memory location. Tables 1-10 and 1-11 contain
the sequences that depend on an index register or on the contents of a memory
location alone.
26 6502 ASSEMBLY LANGUAGE SUBROUTINES
Conditional
Condition Flag-Setting Instruction
Branch
Conditional
Condition Flag-Setting Instruction
Branch
Conditional
Condition Flag-Setting Instruction
Branch
Conditional
Condition Flag-Setting Instruction (s)
Branch
LOOPING
The simplest way to implement a loop (that is, repeat a sequence of instruc
tions) with the 6502 microprocessor is as follows:
1. Load an index register or memory location with the number of times the
sequence is to be executed.
instructions to be repeated
DEX
BNE LOOP
Nothing except clarity stops us from counting up (using INX, INY, or INC); of
course, you must change the initialization appropriately. As we will see later, a
16-bit counter is much easier to increment than it is to decrement. In any case,
the instructions to be repeated must not interfere with the counting of the repeti
tions. You can store the counter in either index register or any memory location.
Index register X's special features are its use in preindexing and the wide
availability of zero page indexed modes. Index register Y's special feature is its
use in postindexing. As usual, memory locations on page 0 are shorter and faster
to use than are memory locations on other pages.
Of course, if you use an index register or a single memory location as a
counter, you are limited to 256 repetitions. You can provide larger numbers of
repetitions by nesting loops that use a single register or memory location or by
using a pair of memory locations as illustrated in the following examples:
• Nested loops
LDX #NTIMM ;START OUTER COUNTER
LOOPO LDY #NTIML ;START INNER COUNTER
LOOPI
•
instructions to be repeated
The outer loop restores the inner counter (index register Y) to its starting value
CHAPTER 1: GENERAL PROGRAMMING METHODS 29
(NTIML) after each decrement of the outer counter (index register X). The nest
ing produces a multiplicative factor — the instructions starting at LOOPI are
repeated NTIMM x NTIML times. Of course, a more general (and more reasona
ble) approach would use two memory locations on page 0 instead of two index
registers.
The idea here is to increment only the less significant byte unless there is a carry
to the more significant byte. Note that we can recognize a carry only by checking
the Zero flag, since INC does not affect the Carry flag. Counting up is much
simpler than counting down; the comparable sequence for decrementing a 16-bit
counter is
LDA NTIML ;IS LSB OF COUNTER ZERO?
BNE CNTLSB
DEC NTIMH ;YES, BORROW FROM MSB
CNTLSB DEC NTIML ;DECREMENT LSB OF COUNTER
BNE LOOP ;CONTINUE IF LSB HAS NOT REACHED ZERO
LDA NTIMH ;OR IF MSB HAS NOT REACHED ZERO
BNE LOOP
ARRAY MANIPULATION
1. Manipulate the element by indexing from the starting address of the array.
2. Access the succeeding element (at the next higher address) by increment
ing the index register using INX or INY, or access the preceding element (at the
next lower address) by decrementing the index register using DEX or DEY. One
30 6502 ASSEMBLY LANGUAGE SUBROUTINES
could also change the base; this is simple if the base is an absolute address, but
awkward if it is an indirect address.
• Add an element of an array to the accumulator. The base address of the array
is a constant BASE. Update index register X so that it refers to the succeeding 8-
bit element.
ADC BASE,X ;ADD CURRENT ELEMENT
INX ;ADDRESS NEXT ELEMENT
• Load the accumulator with the 35th element of an array. Assume that the
starting address of the array is BASE.
The most efficient way to process an array is to start at the highest address and
work backward. This is the best approach because it allows you to count the index
register down to 0 and exit when the Zero flag is set. You must adjust the
initialization and the indexed operations slightly to account for the fact that the 0
index is never used. The changes are
Manipulating array elements becomes more difficult if you need more than one
element during each iteration (as in a sort that requires interchanging of ele
ments), if the elements are more than one byte long, or if the elements are them
selves addresses (as in a table of starting addresses). The basic problem is the lack
of 16-bit registers or 16-bit instructions. The processor can never be instructed to
handle more than 8 bits. Some examples of more general array manipulation are
The single instruction LDA BASE+1,X loads the accumulator from the same
address as the sequence
INX
LDA BASE,X
assuming that X did not originally contain FF16. If, however, we are using a base
address indirectly, the alternatives are
INC PGZRO ;INCREMENT BASE ADDRESS
SNE INDEX
INC PGZRO+1 ;WITH CARRY IF NECESSARY
INDEX LDA (PGZRO),Y
or
INY
LDA (PGZRO),Y
The second sequence is much shorter, but the first sequence will handle arrays
that are more than 256 bytes long.
• Exchange an element of an array with its successor if the two are not already
in descending order. Assume that the elements are 8-bit unsigned numbers. The
base address of the array is BASE and the index of the first number is in index
register X.
• Load the accumulator from the 12th indirect address in a table. Assume that
the table starts at the address BASE.
LDX #24 ;GET DOUBLED OFFSET FOR INDEX
LDA BASE,X ;GET LSB OF ADDRESS
STA PGZRO ;SAVE ON PAGE ZERO
INX
LDA BASE,X ;GET MSB OF ADDRESS
STA PGZRO+1 ;SAVE ON PAGE ZERO
LDY #0
LDA (PGZRO),Y ;LOAD INDIRECT BY INDEXING WITH ZERO
Note that you must double the index to handle tables containing addresses, since
each 16-bit address occupies two bytes of memory.
If the entire table is on page 0, we can use the preindexed (indexed indirect)
addressing mode.
LDX #24 ;GET DOUBLED OFFSET FOR INDEX
LDA (BASE,X) ;LOAD FROM INDEXED INDIRECT ADDRESS
You still must remember to double the index. Here we must also initialize the
table of indirect addresses in the RAM on page 0.
We can generalize array processing by storing the base address in two locations
on page 0 and using the postindexed (indirect indexed) addressing mode. Now
the base address can be a variable. This mode assumes the use of page 0 and index
register Y and is available only for a limited set of instructions.
Examples
1. Add an element of an array to the accumulator. The base address of the
array is in memory locations PGZRO and PGZRO+1. The index of the element
is in index register Y. Update index register Y so that it refers to the succeeding 8-
bit element.
CLC ADC (PGZRO),Y ;ADD CURRENT ELEMENT
INY ;ADDRESS NEXT ELEMENT
Postindexing also lets us handle arrays that occupy more than 256 bytes. As we
noted earlier, the simplest approach to long counts is to keep a 16-bit comple
mented count in two memory locations. If the array is described by a base address
on page 0, we can update that base whenever we update the more significant byte
of the complemented count. For example, if we want to clear an area of memory
CHAPTER 1: GENERAL PROGRAMMING METHODS 33
The idea here is to proceed to the next page by incrementing the more significant
byte of the indirect address when we finish a 256-byte section.
One can also simplify array processing by reducing the multiplications required
in indexing to additions. In particular, one can handle arrays of two-byte elements
by using ASL A to double an index in the accumulator.
Example
Load the accumulator from the indirect address indexed by the contents of
memory location INDEX. Assume that the table starts at address BASE.
As before, if the entire table of indirect addresses is on page 0, we can use the
preindexed (indexed indirect) addressing mode.
LDA INDEX ;GET INDEX
ASL A ;DOUBLE INDEX FOR 2-BYTE ENTRIES
TAX
LDA (BASE,X) ;LOAD FROM INDEXED INDIRECT ADDRESS
You can handle indexing into longer arrays by using the postindexed (indirect
indexed) mode. Here we must construct a base address with an explicit addition
before indexing, since the 6502's index registers are only 8 bits long.
Example
Load the accumulator from the element of an array defined by a starting
address BASE (BASEH more significant byte, BASEL less significant byte) and a
16-bit index in memory locations INDEX and INDEX+1 (MSB in INDEX-f 1).
34 6502 ASSEMBLY LANGUAGE SUBROUTINES
TABLE LOOKUP
• Load the accumulator with an element from a table. Assume that the base
address of the table is BASE (a constant) and the 8-bit index is in memory loca
tion INDEX.
• Load the accumulator with an element from a table. Assume that the base
address of the table is BASE (a constant, made up of bytes BASEH and BASEL)
and the 16-bit index is in memory locations INDEX and INDEX+1 (MSB in
INDEX + 1).
The procedure is the same one we just showed for an array. You must add the
more significant byte of the index to the more significant byte of the base with an
explicit addition. You can then use postindexing to obtain the element.
• Load memory locations POINTH and POINTL with a 16-bit element from a
table. Assume that the base address of the table is BASE (a constant) and the
index is in memory location INDEX.
We can also handle the case in which the base address is a variable in two memory
locations on page 0 (PGZRO and PGZRO+1).
CHAPTER 1: GENERAL PROGRAMMING METHODS 35
We can revise the program further to handle an array with more than 128 entries.
LDA INDEX ;GET THE INDEX
ASL A ;DOUBLE IT FOR TWO-BYTE ENTRIES
BCC LDELEM
INC PGZRO+1 ;ADD CARRY TO INDIRECT ADDRESS
LDELEM TAY
LDA (PGZRO),Y ;GET LSB OF ELEMENT
STA POINTL
INY
LDA (PGZRO),Y ;GET MSB OF ELEMENT
STA POINTH
Here there are two options: Store the address obtained from the table in two
memory locations and use an indirect jump, or store the address obtained from
the table in the stack and use the RTS (Return from Subroutine) instruction.
JMP is the only 6502 instruction that has true indirect addressing. Note that
TEMP and TEMP+1 can be anywhere in memory; they need not be on page 0.
• RTS adds 1 to the program counter after loading it from the stack. Thus, the
addresses in the table must all be one less than the actual values to which you
wish to transfer control. This offset evidently speeds the processor's execution of
the JSR (Jump to Subroutine) instruction, but it also can confuse the pro
grammer.
• You must remember that the stack is growing down in memory, toward
lower addresses. To have the destination address end up in its normal order (less
significant byte at lower address), we must push the more significant byte first.
This is essentially a double negative; we store the address in the wrong order but
it ends up right because the stack is growing down.
• The use of RTS is confusing. How can one return from a routine that one has
never called? In fact, this approach uses RTS to call a subroutine. You should
remember that RTS is simply a jump instruction that obtains the new value for
the program counter from the top of the stack. While the common use of RTS is
to transfer control from a subroutine back to a main program (hence, the
mnemonic), there is no reason to limit it to that function. The mnemonic may
confuse the programmer, but the microprocessor does exactly what it is supposed
to do. Careful documentation can help calm the nerves if you feel uneasy about
this procedure.
The common uses of jump tables are to implement CASE statements (for
example, multiway branches as used in languages such as FORTRAN, Pascal,
CHAPTER 1: GENERAL PROGRAMMING METHODS 37
and PL/I) to decode commands from a keyboard, and to respond to function keys
on a terminal.
CHARACTER MANIPULATION
or
LDX #$FF ;POINT TO BYTE BEFORE START
LDA #' ;GET A BLANK FOR COMPARISON
EXAMC INX ;PROCEED TO NEXT CHARACTER
CMP STRNG,X ;IS IT A BLANK?
BEQ EXAMC ;YES, KEEP LOOKING
CODE CONVERSION
You can convert data from one code to another using arithmetic or logical
operations (if the relationship is simple) or lookup tables (if the relationship is
complex).
38 6502 ASSEMBLY LANGUAGE SUBROUTINES
Examples
1. Convert an ASCII digit to its binary-coded decimal (BCD) equivalent.
SEC ;CLEAR THE INVERTED BORROW
SBC #'0 ;CONVERT ASCII TO BCD
Since the ASCII digits form an ordered subsequence, all you must do is subtract
the offset (ASCII 0).
You can also clear bit positions 4 and 5 with the single instruction
AND #%11001111 :CONVERT ASCII TO BCD
Either the arithmetic sequence or the logical instruction will, for example, con
vert ASCII 0 (3016) to decimal 0 (0016).
2. Convert a binary-coded decimal (BCD) digit to its ASCII equivalent.
CLC ;CLEAR THE CARRY
ADC #'0 ;CONVERT BCD TO ASCII
The inverse conversion is equally simple. You can also set bit positions 4 and 5
with the single instruction
ORA #%00110000 ;CONVERT BCD TO ASCII
Either the arithmetic sequence or the logical instruction will, for example, con
vert decimal 6 (06i6) to ASCII 6 (3616).
3. Convert one 8-bit code to another using a lookup table. Assume that the
lookup table starts at address NEWCD and is indexed by the value in the original
code (for example, the 27th entry is the value in the new code corresponding to
27 in the original code). Assume that the data is in memory location CODE.
LDX CODE ;GET THE OLD CODE
LDA NEWCD,X ;CONVERT IT TO THE NEW CODE
MULTIPLE-PRECISION
ARITHMETIC
• Clear the Carry before starting addition or set the Carry before starting
subtraction, since there is never a carry into or borrow from the least significant
byte.
• Use the Add with Carry (ADC) or Subtract with Borrow (SBC) instruction
to perform an 8-bit operation and include the carry or borrow from the previous
operation.
CHAPTER 1: GENERAL PROGRAMMING METHODS 39
MULTIPLICATION AND
DIVISION
Examples
1. Multiply the contents of the accumulator by 2.
ASL A ;DOUBLE A
This approach assumes that shifting the accumulator left never produces a
carry. This approach is often handy in determining the locations of elements of
two-dimensional arrays. For example, let us assume that we have a set of tem
perature readings taken at four different positions in each of three different tanks.
We organize the readings as a two-dimensional array T(I,J), where I is the tank
number (1, 2, or 3) and J is the number of the position in the tank (1, 2, 3, or 4).
We store the readings in the linear memory of the computer one after another as
follows, starting with tank 1:
BASE T(l,l) Reading at tank 1, location 1
BASE+1 T(l,2) Reading at tank 1, location 2
BASE+2 T(l,3) Reading at tank 1, location 3
BASE+3 T(l,4) Reading at tank 1, location 4
BASE+4 T(2,l) Reading at tank 2, location 1
BASE+5 T(2,2) Reading at tank 2, location 2
BASE+6 T(2,3) Reading at tank 2, location 3
BASE+7 T(2,4) Reading at tank 2, location 4
BASE+8 T(3,l) Reading at tank 3, location 1
BASE+9 T(3,2) Reading at tank 3, location 2
BASE+10 T(3,3) Reading at tank 3, location 3
BASE+11 T(3,4) Reading at tank 3, location 4
40 6502 ASSEMBLY LANGUAGE SUBROUTINES
Example
Divide the contents of the accumulator by 4.
LSR A ;DIVIDE BY 2
LSR A ;AND BY 2 AGAIN
If you are multiplying or dividing signed numbers, you must be careful to sepa
rate the signs from the magnitudes. You must replace logical shifts with
arithmetic shifts that preserve the value of the sign bit.
• Algorithms involving shifts and additions (multiplication) or shifts and
subtractions (division) can be used as described in Chapter 6.
• Lookup tables can be used as discussed previously in this chapter.
LIST PROCESSING5
Lists can be processed like arrays if the elements are stored in consecutive
addresses. If the elements are queued or chained, however, the limitations of the
instruction set are evident in that
CHAPTER 1: GENERAL PROGRAMMING METHODS 41
Examples
1. Retrieve an address stored starting at the address in memory locations
PGZRO and PGZRO+1. Place the retrieved address in memory locations
POINTL and POINTH.
This procedure allows you to move from one element to another in a linked list.
2. Retrieve data from the address currently in memory locations PGZRO and
PGZRO-hi and increment that address by 1.
LDY #0 ;INDEX = ZERO
LDA (PGZRO),Y ;GET DATA USING POINTER
INC PGZRO ;UPDATE POINTER BY 1
BNE DONE
INC PGZRO+1
DONE NOP
This procedure allows you to use the address in memory as a pointer to the next
available location in a buffer. Of course, you can also leave the pointer fixed and
increment a buffer index. If that index is in memory location BUFIND, we have
This procedure lets you build a list of addresses. Such a list could be used, for
example, to write threaded code in which each routine concludes by transferring
control to its successor. The list could also contain the starting addresses of a
series of test procedures or tasks or the addresses of memory locations or I/O
devices assigned by the operator to particular functions. Of course, some lists
may have to be placed on page 0 in order to use the 6502's preindexed or postin-
dexed addressing modes.
More general data structures can be processed using the procedures that we
have described for array manipulation, table lookup, and list processing. The key
limitations in the instruction set are the same ones that we mentioned in the dis
cussion of list processing.
Examples
1. Queues or linked lists. Assume that we have a queue header consisting of
the address of the first element in memory locations HEAD and HEAD +1 (on
page 0). If there are no elements in the queue, HEAD and HEAD +1 both con
tain 0. The first two locations in each element contain the address of the next ele
ment or 0 if there is no next element.
• Add the element in memory locations PGZRO and PGZRO+1 to the head
of the queue.
• Remove an element from the head of the queue and set the Zero flag if no
element is available.
STA PGZRO+1
ORA PGZRO ;ANY ELEMENTS IN QUEUE?
BEQ DONE ;NO, DONE (LINK = 0000)
LDA (PGZRO),Y ;YES, MAKE NEXT ELEMENT NEW HEAD
STA (HEAD),Y
DEY
LDA (PGZRO),Y
STA (HEAD),Y
INY ;CLEAR ZERO FLAG BY MAKING Y 1
DONE NOP
to test the 16-bit number in memory locations ADDR and ADDR+1. The Zero
flag is set only if both bytes are 0.
2. Stacks. Assume that we have a stack structure consisting of 8-bit elements.
The address of the next empty location is in addresses SPTR and SPTR+1 on
page 0. The lowest address that the stack can occupy is LOW and the highest
address is HIGH.
• If the stack overflows, clear the Carry flag and exit. Otherwise, store the
accumulator in the stack and increase the stack pointer by 1. Overflow means that
the stack has exceeded its area.
LDA #HIGHL :STACK POINTER GREATER THAN HIGH?
CMP SPTR
LDA #HIGHM
SBC SPTR+1
BCC EXIT ;YES, CLEAR CARRY AND EXIT (OVERFLOW)
LDY #0 ;NO STORE ACCUMULATOR IN STACK
STA (SPTR),Y
INC SPTR ;INCREMENT STACK POINTER
BNE EXIT
INC SPTR+1
EXIT NOP
• If the stack underflows, set the Carry flag and exit. Otherwise, decrease the
stack pointer by 1 and load the accumulator from the stack. Underflow means
that there is nothing left in the stack.
LDA #LOWL ;STACK POINTER AT OR BELOW LOW?
CMP SPTR
LDA #LOWM
SBC SPTR+1
BCS EXIT ;YES, SET CARRY AND EXIT (UNDERFLOW)
LDA SPTR ;NO, DECREMENT STACK POINTER
BNE NOBOR
DEC SPTR+1
NOBOR DEC SPTR
LDY #0 :LOAD ACCUMULATOR FROM STACK
LDA (SPTR),Y
EXIT NOP
44 6502 ASSEMBLY LANGUAGE SUBROUTINES
The most common ways to pass parameters on the 6502 microprocessor are
1. In registers. Three 8-bit registers are available (A, X, and Y). This
approach is adequate in simple cases but it lacks generality and can handle only a
limited number of parameters. The programmer must remember the normal uses
of the registers in assigning parameters. In other words,
• The accumulator is the obvious place to put a single 8-bit parameter.
• Index register X is the obvious place to put an index, since it is the most
accessible and has the most instructions that use it for addressing. Index register
X is also used in preindexing (indexed indirect addressing).
• Index register Y is used in postindexing (indirect indexed addressing).
This approach is reentrant as long as the interrupt service routines save and
restore all the registers.
• The starting address of the memory area minus 1 is at the top of the stack.
That is, the starting address is the normal return address, which is 1 larger than
the address the 6502's JSR instruction saves in the stack. You can move the start
ing address to memory locations RETADR and RETADR+1 on page 0 with the
following sequence:
PLA ;GET LSB OF RETURN ADDRESS
STA RETADR
PLA ;GET MSB OF RETURN ADDRESS
STA RETADR+1
INC RETADR ;ADD 1 TO RETURN ADDRESS
BNE DONE
INC RETADR+1
DONE NOP
CHAPTER 1: GENERAL PROGRAMMING METHODS 45
Now we can access the parameters through the indirect address. That is, you can
load the accumulator with the first parameter by using the sequence
LDY #0 ;INDEX = ZERO
LDA (RETADR),Y ;LOAD FIRST PARAMETER
An obvious alternative is to leave the return address unchanged and start the
index at 1. That is, we would have
0
Now we could load the accumulator with the first parameter by using the
sequence
LDY #1 ;INDEX = 1
LDA (RETADR),Y ;LOAD FIRST PARAMETER
• All parameters must be fixed for a given call, since the program memory is
typically ROM.
• The subroutine must calculate the actual return address (the address of the
last byte in the parameter area) and place it on top of the stack before executing a
Return from Subroutine (RTS) instruction.
Example
Assume that subroutine SUBR requires an 8-bit parameter and a 16-bit
parameter. Show a main program that calls SUBR and contains the required
parameters. Also show the initial part of the subroutine that retrieves the
parameters, storing the 8-bit item in the accumulator and the 16-bit item in
memory locations PGZRO and PGZRO+1, and places the correct return address
at the top of the stack.
Subroutine call
JSR SUBR ;EXECUTE SUBROUTINE
.BYTE PAR8 ;8-BIT PARAMETER
.WORD PAR16 ;16-BIT PARAMETER
... next instruction ...
Subroutine
SUBR PLA ;GET LSB OF PARAMETER ADDRESS
STA RETADR
PLA ;GET MSB OF PARAMETER ADDRESS
STA RETADR+1
LDY #1 ;ACCESS FIRST PARAMETER
LDA (RETADR)fY
TAX
INY
LDA (RETADR),Y ;ACCESS LSB OF 16-BIT PARAMETER
STA PGZRO
INY
LDA (RETADR).Y ;GET MSB OF 16-BIT PARAMETER
46 6502 ASSEMBLY LANGUAGE SUBROUTINES
STA PGZRO+1
LDA RETADR ;CALCULATE ACTUAL RETURN ADDRESS
CLC
ADA #3
TAY
BCC STRMSB
INC RETADR+1
STRMSB LDA RETADR+1 ;PUT RETURN ADDRESS ON TOP OF STACK
PHA
TYA
PHA
The initial sequence pops the return address from the top of the stack (JSR saved
it there) and stores it in memory locations RETADR and RETADR+1. In fact,
the return address does not contain an instruction; instead, it contains the first
parameter. Remember that JSR actually saves the return address minus 1; that is
why we must start the index at 1 rather than at 0. Finally, adding 3 to the return
address and saving the sum in the stack lets a final RTS instruction transfer con
trol back to the instruction following the parameters.
This approach allows parameters lists of any length. However, obtaining the
parameters from memory and adjusting the return address is awkward at best; it
becomes a longer and slower process as the number of parameters increases.
4. In the stack. If you use this approach, you must remember the following:
• JSR stores the return address at the top of the stack. The parameters that the
calling routine placed in the stack begin at address Olss + 3, where ss is the con
tents of the stack pointer. The 16-bit return address occupies the top two loca
tions and the stack pointer itself always refers to the next empty address, not the
last occupied one. Before the subroutine can obtain its parameters, it must
remove the return address from the stack and save it somewhere.
• The only way for the subroutine to determine the value of the stack pointer is
by using the instruction TSX. After TSX has been executed, you can access the
top of the stack by indexing with register X from the base address 010116. The
extra offset of 1 is necessary because the top of the stack is empty.
• The calling program must place the parameters in the stack before calling the
subroutine.
or
Either approach leaves NRESLT empty locations at the top of the stack as shown
in Figure 1-5. Of course, if NRESLT is 1 or 2, simply executing PHA the
appropriate number of times will be much faster and shorter. The same
approaches can be used to provide stack locations for temporary storage.
Example
Assume that subroutine SUBR requires an 8-bit parameter and a 16-bit
parameter, and that it produces two 8-bit results. Show a call of SUBR, the
removal of the return address from the stack, and the cleaning of the stack after
the return. Figure 1-6 shows the appearance of the stack initially, after the
subroutine call, and at the end. If you always use the stack for parameters and
results, you will generally keep the parameters at the top of the stack in the proper
order. Then you will not have to save the parameters or assign space in the stack
for the results (they will replace some or all of the original parameters). You will,
however, have to assign space on the stack for temporary storage to maintain
generality and reentrancy.
Calling program
Subroutine
Olss -NRESLT
Empty space
for storing
results in the
Olss stack
Stack Stack
Pointer Pointer
Olss -7
LSB of return
address
MSB of return
address
8-bit parameter
LSB of 16-bit
parameter
SIMPLE INPUT/OUTPUT
Simple input/output can be performed using any memory addresses and any
instructions that reference memory. The most common instructions are the
following:
• LDA (load accumulator) transfers eight bits of data from an input port to the
accumulator.
• STA (store accumulator) transfers eight bits of data from the accumulator to
an output port.
Other instructions can combine the input operation with arithmetic or logical
operations. Typical examples are the following:
• AND logically ANDs the contents of the accumulator with the data from an
input port.
• BIT logically ANDs the contents of the accumulator with the data from an
input port but does not store the result anywhere. It does, however, load the
Negative flag with bit 7 of the input port and the Overflow flag with bit 6, regard
less of the contents of the accumulator.
• CMP subtracts the data at an input port from the contents of the accumula
tor, setting the flags but leaving the accumulator unchanged.
Instructions that operate on data in memory can also be used for input or out
put. Since these instructions both read and write memory, their effect on input
and output ports may be difficult to determine. Remember, input ports cannot
generally be written, nor can output ports generally be read. The commonly used
instructions are the following:
• ASL shifts its data to the left, thus moving bit 7 to the Carry for possible
serial input.
• DEC decrements its data. Among other effects, this inverts bit 0.
• INC increments its data. Among other effects, this inverts bit 0.
• LSR shifts its data to the right, thus moving bit 0 to the Carry for possible
serial input.
• ROR rotates its data to the right, thus moving the old Carry to bit 7 and mov
ing bit 0 to the Carry.
• ROL rotates its data to the left, thus moving the old Carry to bit 0 and mov
ing bit 7 to the Carry.
The effects of these instructions on an input port are typically similar to their
effects on a ROM location. The microprocessor can read the data, operate on it,
and set the flags, but it cannot store the result back into memory. The effects on
50 6502 ASSEMBLY LANGUAGE SUBROUTINES
an output port are even stranger, unless the port is latched and buffered. If it is
not, the data that the processor reads is system-dependent and typically has no
connection with what was last stored there.
Examples
1. Perform an 8-bit input operation from the input port assigned to memory
address B00016.
LDA $B000 ;INPUT DATA
3. Set the Zero flag if bit 5 of the input port assigned to memory address
75D016is0.
LDA #%00100000 ;GET MASK
AND $75D0 ;SET FLAG IF BIT 5 IS ZERO
If the bit position of interest is number 6, we can use the single instruction
BIT $75D0
4. Set the Zero flag if the data at the input port assigned to memory address
1700l6islB16.
LDA #$1B
CMP $170U
5. Load the Carry flag with the data from bit 7 of the input port assigned to
memory address 33A5I6.
ASL $33A5
Note that this instruction does not change the data in memory location 33A516
unless that location is latched and buffered. If, for example, there are eight simple
switches attached directly to the port, the instruction will surely have no effect on
whether the switches are open or closed.
6. Place a logic 1 in bit 0 of the output port assigned to memory address B070l6.
LDA $B070
ORA #%00000001
STA $B070
If none of the other bits in address B07016 are connected, we can use the sequence
SEC
ROL $B070
CHAPTER 1: GENERAL PROGRAMMING METHODS 51
If we know that bit 0 of address B07016 is currently a logic 0, we can use the single
instruction
INC $B070
All of these alternatives will have strange effects if memory address B070l6 can
not be read. The first two will surely make bit 0 a logic 1, but their effects on the
other bits are uncertain. The outcome of the third alternative would be a total
mystery, since we would have no idea what is being incremented. We can avoid
the uncertainty by saving a copy of the data in RAM location TEMP. Now we can
operate on the copy using
LDA TEMP ;GET COPY OF OUTPUT
ORA #%00000G01 ;SET BIT 0
STA $B070 ;OUTPUT NEW DATA
STA TEMP ;AND SAVE A COPY OF IT
LOGICAL AND
PHYSICAL DEVICES
One way to select I/O devices by number is to use an I/O device table. An I/O
device table assigns the actual I/O addresses (physical devices) to the device num
bers (logical devices) to which a program refers. Using this method, a program
written in a high-level language may refer to device number 2 for input and num
ber 5 for output. For testing purposes, the operator may assign devices numbers 2
and 5 to be the input and output ports, respectively, of his or her terminal. For
normal stand-alone operation, the operator may assign device number 2 to be an
analog input unit and device number 5 the system printer. If the system is to be
operated by remote control, the operator may assign devices numbers 2 and 5 to
be communications units used for input and output.
One way to provide this distinction between logical and physical devices is to
use the 6502's indexed indirect addressing or preindexing. This mode assumes
that the device table is located on page 0 and is accessed via an index in register X.
If we have a device number in memory location DEVNO, the following programs
can be used:
• Load the accumulator from the device number given by the contents of
memory location DEVNO.
LDA DEVNO ;GET DEVICE NUMBER
ASL A ;DOUBLE IT TO HANDLE DEVICE ADDRESSES
TAX
LDA (DEVTAB,X) ;GET DATA FROM DEVICE
In both cases, we assume that the I/O device table starts at address DEVTAB (on
page 0) and consists of 2-byte addresses. Note that the 6502 provides an appropri
ate addressing method, but does not produce any error messages if the pro
grammer uses that method improperly by accessing odd addresses or by indexing
off the end of page 0 (the processor does provide automatic wraparound). In real
applications (see Chapter 10), the device table will probably contain the starting
addresses of I/O subroutines (drivers) rather than actual device addresses.
You can handle status and control signals like any other data. The only special
problem is that the processor may not be able to read output ports; in that case,
you must retain copies (in RAM) of the data sent to those ports.
Examples
1. Branch to address DEST if bit 3 of the input port assigned to memory
address A100l6 is 1.
If address CNTL cannot be read properly, we can use a copy in memory address
TEMP.
You must update the copy every time you change the data.
Retaining copies of the data in memory (or using the values stored in a latched,
buffered output port) allows you to change part of the data without affecting other
parts that may have unrelated meanings. For example, you could change the state
of one indicator light (for example, a light that indicated local or remote opera
tion) without affecting other indicator lights attached to the same port. You could
similarly change one control line (for example, a line that determined whether
motion was in the positive or negative X-direction) without affecting other con
trol lines attached to the same port.
PERIPHERAL CHIPS
The major peripheral chips in 6502-based microcomputers are the 6520 and
6522 parallel interfaces (known as the Peripheral Interface Adapter or PIA and
the Versatile Interface Adapter or VIA, respectively), the 6551 and 6850 serial
interfaces (known as Asynchronous Communications Interface Adapters or
ACIAs), and the 6530 and 6532 multifunction devices (known as ROM-I/O-
timers or RAM-I/O-timers or ROM-RAM-I/O-timers, abbreviated RIOT or
RRIOT and sometimes called combo chips). All of these devices can perform a
variety of functions, much like the microprocessor itself. Of course, peripheral
chips perform fewer different functions than processors and the range of func
tions is limited. The idea behind programmable peripheral chips is that each con
tains many useful circuits; the designer selects the ones he or she wants to use by
storing one or more selection codes in control registers, much as one selects a
particular circuit from a Designer's Casebook by turning to a particular page. The
advantages of programmable chips are that a single board containing such devices
can handle many applications and changes, or, corrections can be made by chang
ing selection codes rather than by redesigning circuit boards. The disadvantages
54 6502 ASSEMBLY LANGUAGE SUBROUTINES
of programmable chips are the lack of standards and the difficulty of determining
how specific chips operate.
Chapter 10 contains typical initialization routines for the 6520, 6522, 6551,
6850, 6530, and 6532 devices. These devices are also discussed in detail in the
Osborne 4 and 8-Bit Microprocessor Handbook1. We will provide only a brief over
view of the 6522 device here, since it is the most widely used. 6522 devices are
used, for example, in the Rockwell AIM-65, Synertek SYM-1, Apple, and other
popular microcomputers as well as in add-on I/O boards and other functions
available from many manufacturers.
A VIA contains two 8-bit parallel I/O ports (A and B), four status and control
lines (CA1, CA2, CB1, and CB2 - two for each of the two ports), two 16-bit
counter/timers (timer 1 and timer 2), an 8-bit shift register, and interrupt logic.
Each VIA occupies 16 memory addresses. The RS (register select) lines choose
the various internal registers as described in Table 1-12. The way that a VIA oper
ates is determined by the values that the program stores in four registers.
• The Auxiliary Control Register (ACR) determines whether the input data
ports are latched and how the timers and shift register operate. Figure 1-8 con
tains the bit assignments for this register. We will also discuss briefly the purposes
of these bits and their uses in common applications.
CHAPTER 1: GENERAL PROGRAMMING METHODS 55
Select Lines
Label Addressed Location
RSO
RSI
Figure 1-7: Bit Assignments for the 6522 VIA's Peripheral Control Register
7 6 5 4 3 2 I 0 Bit Number
Figure 1-8: Bit Assignments for the 6522 VIA's Auxiliary Control Register
CHAPTER 1: GENERAL PROGRAMMING METHODS 57
In order to initialize a VIA properly, we must know what its start-up state is.
Reset clears all the VIA registers, thus making all the data and control lines
inputs, disabling all latches, interrupts, and other functions, and clearing all
status bits.
The data direction registers are easy to initialize. Typical routines are
• Input from a keyboard. When the operator presses a key, the keyboard pro
duces a parallel code corresponding to the key and a transition on the DATA
READY or VALID DATA line. The computer must determine that the transi
tion has occurred, read the data, and produce a transition on the DATA
ACCEPTED line to indicate that the data has been read.
latest data. The computer must determine that the printer is ready, send the data,
and produce a transition on the DATA READY line to indicate that new data is
available. Of course, input and output are in some sense mirror images. In the
input case, the peripheral is the sender and the computer is the receiver; in the
output case, the computer is the sender and the peripheral is the receiver.
Thus, a chip intended for handshaking functions must provide the following
functions:
• It must provide an indication that the transition has occurred in a form that is
easy for the computer to handle.
• It must allow for the production of the response — that is, for the computer
to indicate DATA ACCEPTED to an input peripheral or DATA READY to an
output peripheral.
There are some obvious variations that the handshaking chip should allow for,
including the following:
Experience has shown that the handshaking chip can provide still more conve
nience, at virtually no cost, in the following ways:
• It can clear the status latches automatically when an input port is read or an
output port is written, thus preparing them for the next operation.
• In cases where the peripherals are simple switches or lights and do not need
CHAPTER 1: GENERAL PROGRAMMING METHODS 59
any status or control signals, the chip should allow independent operation of the
status lines. The designer can then use these lines (which would otherwise be
wasted) for such purposes as threshold detection, zero-crossing detection, or
clock inputs. In such cases, the designer wants the status and control signals to be
entirely independent of the operations on the parallel port. We should not have
any automatic clearing of latches or sending of responses. This is known as an
independent mode.
The 6522 peripheral control register allows the programmer to provide any of
these functions. Bits 0 through 3 govern the operation of port B and its control
signals; bits 4 through 7 govern the operation of port A and its control signals.
The status indicators are in the Interrupt flag register (see Figure 1-9). We may
characterize the bits in the control register as follows:
• Bit 0 (for port A) and bit 4 (for port B) determine whether the active transi
tion on control line 1 is high-to-low (0) or low-to-high (1). If control line 2 is an
extra input, bit 2 (for port A) and bit 6 (for port B) has a similar function.
• If control line 2 is an extra input, bit 1 (for port A) and bit 5 (for port B)
determine whether it operates independently of the parallel data port. This bit is 0
for normal handshaking and 1 for independent operation.
• Bit 3 (for port A) and bit 7 (for port B) determine whether control line 2 is an
extra input line (0) or an output response (1).
• If control line 2 is an output response, bit 2 (for port A) and bit 6 (for port B)
determine whether it operates in an automatic mode (0) or a manual mode (1).
• If control line 2 is being operated in the automatic mode, bit 1 (for port A)
and bit 5 (for port B) determine whether the response lasts for one clock cycle (1)
or until the peripheral produces another active transition on control line 1 (0).
• If control line 2 is being operated in the manual mode, bit 1 (for port A) and
bit 5 (for port B) determine its level.
-Bit Number
Bit
Set by Cleared by
Number
Bits 0, 1, 3, and 4 are the I/O handshake signals. Bit 7 (IRQ) is 1 if any of the
interrupts is both active and enabled.
These sequences do not depend on the contents of the peripheral control register,
since they do not change any of the bits except the one that controls the response.
Tables 1-13 and 1-14 summarize the operating modes for control lines CA2
and CB2. Note that the automatic output modes differ slightly in that port A pro
duces a response after either read or write operations, whereas port B produces a
response only after write operations.
62 6502 ASSEMBLY LANGUAGE SUBROUTINES
Table 1-13: Operating Modes for Control Line CA2 of a 6522 VIA
Pulse Output Mode — CA2 goes low for one cycle following a read or
1 0 1
write of the Peripheral A Output register.
1 1 0 Manual Output Mode — The CA2 output is held low in this mode.
The auxiliary control register is less important than the peripheral control
register. Its bits have the following functions (see Figure 1-8):
• Bits 0 and 1, if set, cause the VIA to latch the input data on port A (bit 0) or
port B (bit 1) when an active transition occurs on control line 1. This option
allows for the case in which the input peripheral provides valid data only briefly,
and the data must be saved until the processor has time to handle it.
• Bits 2, 3, and 4 control the operations of the seldom-used shift register. This
register provides a simple serial I/O capability, but most designers prefer either to
use the serial I/O chips such as the 6551 or 6850 or to provide the entire serial
interface in software.
• Bit 5 determines whether timer 2 generates a single time interval (the so-
called one-shot mode) or counts pulses on line PB6 (pulse-counting mode).
• Bit 6 determines whether timer 1 generates one time interval (0) or operates
continuously (1), reloading its counters from the latches after each interval
elapses.
CHAPTER 1: GENERAL PROGRAMMING METHODS 63
Table 1-14: Operating Modes for Control Line CB2 of a 6522 VIA
Pulse Output Mode — Set CB2 low for one cycle following a write ORB
1 0 1
operation.
1 1 0 Manual Output Mode — The CB2 output is held low in this mode.
1 1 1 Manual Output Mode — The CB2 output is held high in this mode.
The uses of most of these functions are straightforward. They are not as com
mon as the handshaking functions governed by the peripheral control register.
You can also operate a 6522 VIA in an interrupt-driven mode. Interrupts are
enabled or disabled by setting bits in the interrupt enable register (see Figures 1-
10 and 1-11) with bit 7 (the enable/disable flag) set (for enabling) or cleared (for
disabling). Interrupts can be recognized by examining the interrupt flag register
(see Figure 1-9). Table 1-15 summarizes the setting and clearing (resetting) of
interrupt flags on the 6522 VIA.
64 6502 ASSEMBLY LANGUAGE SUBROUTINES
7 6 5 4 3 2 10 -Bit Number
The Interrupt Flag register identifies those interrupts which are active.
A 1 in any bit position indicates an active interrupt, whereas a 0 indicates
an inactive interrupt.
Figure 1-10: The 6522 VIA's Interrupt Flag and Interrupt Enable Registers
Enable specified
You can selectively enable or disable individual interrupts via the Interrupt
Enable register. You enable individual interrupts by writing to the Interrupt
Enable register with a 1 in bit 7. Thus you could enable "time out for Timer 1"
and "active transitions of signal CB1" by storing D016 in the Interrupt Enable register:
Set by Cleared by
4 Active transition of the signal on CB1 Reading from or writing to I/O Port B
• By saving the program counter (more significant byte first) and the status
register in the stack in the order shown in Figure 1-12. Note that the status
register ends up on top of the program counter; the sequence PHP, JSR would
produce the opposite order. The program counter value here is the address of the
next instruction; there is no offset of 1 as there is with JSR.
• By disabling the maskable interrupt by setting the I flag in the status register.
• By fetching a destination address from a specified pair of memory addresses
(see Table 1-16) and placing that destination in the program counter.
Thus, the programmer should consider the following guidelines when writing
interrupt-driven code for the 6502:
• The accumulator and index registers must be saved and restored explicitly if
the service routine changes them. Only the status register is saved automatically.
66 6502 ASSEMBLY LANGUAGE SUBROUTINES
Before After
Olss -4 Olss -4
Stack Stack
The service routine must save the accumulator before it saves the index registers,
since it can only transfer an index register to the stack via the accumulator. Typi
cal saving and restoring sequences are
PHA ;SAVE ACCUMULATOR IN STACK
TXA ;SAVE INDEX REGISTER X
PHA
TYA ;SAVE INDEX REGISTER Y
PHA
The order of the index registers does not matter, as long as the saving and restor
ing orders are opposites.
• The interrupt need not be reenabled explicitly, since the RTI (Return from
Interrupt) instruction restores the old status register as part of its execution. This
restores the original state of the Interrupt Disable flag. If you wish to return with
interrupts disabled, you can set the Interrupt Disable flag in the stack with the
sequence
PLA ;GET STATUS REGISTER
ORA #%00000100 ;DISABLE INTERRUPT IN STACK
;PUT STATUS REGISTER BACK IN STACK
CHAPTER 1: GENERAL PROGRAMMING METHODS 67
The addresses are stored in the usual 6502 fashion with the less significant byte
at the lower address.
Note the convenience here of having the status register at the top, rather than
underneath the return address.
• If you have code that the processor must execute with interrupts disabled,
you can use SEI (Set Interrupt Disable) to disable maskable interrupts and CLI
(Clear Interrupt Disable) to enable them afterward. If the section of code could
be entered with interrupts either disabled or enabled, you must be sure to restore
the original state of the Interrupt Disable flag. That is, you must save and restore
the status register as follows:
The alternative (automatically reenabling the interrupts at the end) would cause a
problem if the section were entered with the interrupts already disabled.
• If you want to allow the user to select the actual starting address of the ser
vice routine, place an indirect jump at the vectored address. That is, the routine
starting at the vectored address is simply
This procedure increases the interrupt response time by the execution time of an
indirect jump (five clock cycles).
• You must remember to save and restore incidental information that is essen
tial for the proper execution of the interrupted program. Such incidental informa
tion may include memory locations on page 0, priority registers (particularly if
they are write-only), and other status.
• To achieve general reentrancy, you must use the stack for all temporary
storage beyond that provided by the registers. As we noted in the discussion of
68 6502 ASSEMBLY LANGUAGE SUBROUTINES
parameter passing, you can assign space on the stack (NPARAM bytes) with the
sequence
Later, you can remove the temporary storage area with the sequence
If NPARAM is only 1 or 2, you can replace these sequences with the appropriate
number of push and pull instructions in which the data is ignored.
• The service routine should initialize the Decimal Mode flag with either CLD
or SED if it uses ADC or SBC instructions. The old value of that flag is saved and
restored automatically as part of the status register, but the service routine should
not assume a particular value on entry.
MAKING PROGRAMS
RUN FASTER
- In general, you can make a program run substantially faster by first determin
ing where it is spending its time. This requires that you determine which loops
(other than delay routines) the processor is executing most often. Reducing the
execution time of a frequently executecUoop will have a major effect because of
the multiplying factor. It is thus critical to determine how often instructions are
being executed and to work on loops in the order of their frequency of execution.
Once you have determined which loops the processor executes most fre
quently, you can reduce their execution time with the following techniques:
• Work backward through arrays rather than forward. This allows you to count
the index register down to 0 and use the setting of the Zero flag as an exit condi
tion. No explicit comparison is then necessary. Note that you will have to subtract
1 from the base addresses, since 1 is the smallest index that is actually used.
• Use in-line code rather than subroutines. This will save at least a JSR instruc
tion and an RTS instruction.
• Watch the special uses of the index registers to avoid having to move data
between them. The only register that can be used in indirect indexed addressing
is register Y; the only register that can be used in indexed indirect addressing or
in loading and storing the stack pointer is register X.
• Use the instructions ASL, DEC, INC, LSR, ROL, and ROR to operate
directly on data in memory without moving it to a register.
• Use the BIT instruction to test bits 6 or 7 of a memory location without load
ing the accumulator.
• Use the CPX and CPY instructions to perform comparisons without using
the accumulator.
You can make a program use significantly less memory only by identifying
common sequences of instructions and replacing those sequences with
subroutine calls. The result is a single copy of each sequence. The more instruc
tions you can place in subroutines, the more memory you save. The drawbacks of
this approach are that JSR and RTS themselves require memory and take time to
execute, and that the subroutines are typically not very general and may be
difficult to understand or use. Some sequences of instructions may even be
implemented as subroutines in a monitor or in other systems programs that are
always resident. Then you can replace those sequences with calls to the systems
program as long as the return is handled properly.
Some of the methods that reduce execution time also reduce memory usage.
In particular, using page 0, reorganizing loops, working backward through arrays,
incrementing 16-bit quantities, operating directly on memory, and using special
instructions such as CPX, CPY, and BIT reduce both execution time and
memory usage. Of course, using in-line code rather than loops and subroutines
reduces execution time but increases memory usage.
Lookup tables generally use extra memory but save execution time. Some
ways that you can reduce their memory requirements are by eliminating inter
mediate values and interpolating the results,910 eliminating redundant values
with special tests, and reducing the range of input values. Often you will find that
a few prior tests or restrictions will greatly reduce the size of the required table.
REFERENCES
6. Ibid.
This chapter shows how to implement instructions and addressing modes that
are not included in the 6502's instruction set. Of course, no instruction set can
ever include all possible combinations. Designers must make choices based on
how many operation codes are available, how easily an additional combination
could be implemented, and how often it would be used. A description of addi
tional instructions and addressing modes does not imply that the basic instruction
set is incomplete or poorly designed.
We concentrate our attention on additional instructions and addressing modes
that are
This chapter should be of particular interest to those who are familiar with the
assembly languages of other computers.
following order: byte (8-bit), word (16-bit), decimal, bit, nibble or digit, and
multiple. In describing addressing modes, we use the following order: direct,
indirect, immediate, indexed, register, autopreincrement, autopostincrement,
autopredecrement, autopostdecrement, indirect preindexed (also called prein-
dexed or indexed indirect), and indirect postindexed (also called postindexed or
indirect indexed).
ARITHMETIC INSTRUCTIONS
Addition Instructions
(Without Carry)
A more general approach restores the original value of the D flag; that is,
pHP ;SAVE OLD D FLAG
SED ;ENTER DECIMAL MODE
CLC ;CLEAR CARRY
ADC ADDR ;(A) = (A) + (ADDR) IN DECIMAL
PLP ;RESTORE OLD D FLAG
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 75
Note that restoring the status register destroys the carry from the addition.
9. Add 16-bit number VAL16 (VAL16M more significant byte, VAL16L less
significant byte) to memory locations SUM and SUM + 1 (MSB in SUM + 1).
CLC ;CLEAR CARRY
LDA SUM ;ADD LSB'S WITHOUT CARRY
ADC #VAL16L
STA SUM
LDA SUM+1 ;ADD MSB'S WITH CARRY
ADC #VAL16
STA SUM+1
Addition Instructions
(With Carry)
5. Add 16-bit number VAL16 (VAL16M more significant byte, VAL16L less
significant byte) to memory locations SUM and SUM + 1 (MSB in SUM + 1) with
Carry.
Subtraction Instructions
(Without Borrow)
The Carry flag acts as an inverted borrow, so it must be set to 1 if its value is to
have no effect on the subtraction.
The Carry flag has the same meaning in the decimal mode as in the binary mode.
J. Decimal subtr;
Subtraction in Reverse
Instructions
or
The Carry acts as an inverted borrow in either method; that is, the Carry is set to
1 if no borrow is necessary.
Increment Instructions
or
INX does not affect the Carry flag; it does, however, affect the Zero flag.
3. Increment stack pointer.
TSX ;MOVE S TO X
INX ;THEN INCREMENT X AND RETURN VALUE
TXS
or
TAX ;SAVE A
PLA ;INCREMENT STACK POINTER
TXA ;RESTORE A
Remember that INC and DEC produce binary results even when the D flag is set.
or
STA ADDR
BCC DONE
LDA ADDR+1 ;CARRY TO MSB IF NECESSARY
ADC #0
STA ADDR+1
DONE CLD ;LEAVE DECIMAL MODE
INC produces a binary result even when the Decimal Mode flag is set. Note that
we could eliminate the BCC instruction from the program without affecting the
result, but the change would increase the average execution time.
Decrement Instructions
or
or
DEX does not affect the Carry flag; it does, however, affect the Zero flag.
3. Decrement stack pointer.
TSX ;MOVE S TO X
DEX ;THEN DECREMENT X AND RETURN VALUE
TXS
You can also decrement the stack pointer with PHA or PHP, neither of which
affects any flags.
DEC produces a binary result even when the Decimal Mode flag is set. Note that
we could eliminate the BCS instruction from the program without affecting the
result, but the change would increase the average execution time.
Multiplication Instructions
1. Multiply accumulator by 2.
3. Multiply accumulator by 4.
ASL A ;2 X A
ASL A ;4 X A
TAX ;MOVE TO A
ASL A ;MULTIPLY BY SHIFTING LEFT
TXA ;RETURN RESULT
Division Instructions
The second instruction moves the original sign bit (bit 7) to the Carry flag, so the
final rotate can preserve it. This is known as an arithmetic shift, since it preserves
the sign of the number while reducing its magnitude. The fact that the sign bit is
copied to the right is known as sign extension.
Comparison Instructions
1. Compare VALUE with accumulator bit by bit, setting each bit position that
is different.
EOR #VALUE
Remember, the EXCLUSIVE OR of two bits is 1, if and only if the two bits are
different.
2. Compare memory locations ADR1 and ADR1 +1 (MSB in ADR1 +1) with
memory locations ADR2 and ADR2+1 (MSB in ADR2+1). Set Carry if the
first operand is greater than or equal to the second one (that is, if ADR1 and
ADR1 +1 contain a 16-bit unsigned number greater than or equal to the contents
of ADR2 and ADR2 + 1). Clear Carry otherwise. Set the Zero flag if the two
operands are equal and clear it otherwise,
3. Compare memory locations ADR1 and ADR1 +1 (MSB in ADR1 +1) with
the 16-bit number VAL16 (VAL16M more significant byte, VAL16L less signifi
cant byte). Set Carry if the contents of ADR1 and ADRl + 1 are greater than or
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 85
equal to VAL16 in the unsigned sense. Clear Carry otherwise. Set the Zero flag it*
the contents of ADRl and ADRl +1 are equal to VAL16, and clear it otherwise.
4. Compare memory locations ADRl and ADRl +1 (MSB in ADRl +1) with
memory locations ADR2 and ADR2+1 (MSB in ADR2+1). Set Carry if the
first operand is greater than or equal to the second one in the unsigned sense.
LDA ADRl ;COMPARE LSB*S
CMP ADR2
LDA ADR1+1 ;SUBTRACT MSB'S WITH BORROW
SBC ADR2+1
We use SBC on the more significant bytes in order to include the borrow from the
less significant bytes. This sequence destroys the value in A and sets the Zero flag
only from the final subtraction.
5. Compare memory locations ADRl and ADRl +1 (MSB in ADRl +1) with
the 16-bit number VAL16 (VAL16M more significant byte, VAL16L less signifi
cant byte). Set Carry if the contents of ADRl and ADRl +1 are greater than or
equal to VAL16 in the unsigned sense.
If you want to set the Carry if the contents of ADRl and ADRl +1 are greater
than VAL16, perform the comparison with VAL16+1.
6. Compare stack pointer with the contents of memory location ADDR. Set
Carry if the stack pointer is greater than or equal to the contents of the memory
location in the unsigned sense. Clear Carry otherwise. Set the Zero flag if the two
values are equal and clear it otherwise.
7. Compare stack pointer with the 8-bit number VALUE. Set Carry if the
stack pointer is greater than or equal to VALUE in the unsigned sense. Clear
Carry otherwise. Set the Zero flag if the two values are equal and clear it other
wise.
TSX ;MOVE STACK POINTER TO X
CPX #VALUE ;AND THEN COMPARE
86 6502 ASSEMBLY LANGUAGE SUBROUTINES
Two's Complement
(Negate) Instructions
1. Negate accumulator.
or
4.
i. Nine's
Nine 's complement
compl accumulator (that is, replace A with 99-A).
STA TEMP 7FORM 99-A
LDA #$99
SEC
SBC TEMP
There is no need to bother with the decimal mode, since 99—A is always a valid
BCD number if A originally contained a valid BCD number.
Extend Instructions
The result of the calculation is -(-1+SIGN BIT)-1 =-SIGN BIT. That is,
(ADDR+1) = 00 if A was positive and FF16 if A was negative. An alternative is
88 6502 ASSEMBLY LANGUAGE SUBROUTINES
As in case 2, the result we want is — 1 if the specified bit is 1 and 0 if the specified
bit is 0. That is, we want the negative of the original bit value. The sequence LDA
#$FF, ADC#0 obviously produces the result -l+Carry. The one's comple
ment then gives us the negative of what we had minus 1 (or 1—Carry—1 =
-Carry).
4. Sign function. Replace the value in the accumulator by 00 if it is positive and
by FF16 if it is negative.
LOGICAL INSTRUCTIONS
In this group, we consider logical AND, logical OR, logical EXCLUSIVE OR,
logical NOT (complement), shift, rotate, and test instructions.
MASK has 0 bits in the positions to be cleared and 1 bits in the positions that are
to be left unchanged. For example,
2. Bit test-set the flags according to the value of a bit of memory location
ADDR.
Bits 0 through 5
LDA #MASK
BIT ADDR ;TEST BIT OF ADDR
MASK should have a 1 in the position to be tested and Os everywhere else. The
Zero flag will be set to 1 if the bit tested is 0 and to 0 if the bit tested is 1.
Bits 6 or 7
This single instruction sets the Negative flag to bit 7 of ADDR and the Overflow
flag to bit 6 of ADDR, regardless of the value in the accumulator. Note that the
flags are not inverted as the Zero flag is in normal masking.
Logical OR Instructions
MASK has 1 bits in the positions to be set and 0 bits in the positions that are to be
left unchanged. For example,
2. Test memory locations ADDR and ADDR+1 for 0. Set the Zero flag if
both bytes are 0.
LDA ADDR ;TEST 16-BIT NUMBER FOR ZERO
ORA ADDR+1
The Zero flag is set if and only if both bytes of the 16-bit number are 0. The other
flags are also changed.
Logical EXCLUSIVE OR
Instructions
MASK has 1 bits in the positions to be complemented and 0 bits in the positions
that are to be left unchanged. For example,
Logically EXCLUSIVE ORing the accumulator with all Is inverts all the bits.
3. Compare memory location ADDR with accumulator bit by bit, setting each
bit position that is different.
The EXCLUSIVE OR function is the same as a "not equal" function. Note that
the Negative (Sign) flag is 1 if the two operands have different values in bit posi
tion 7.
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 91
The EXCLUSIVE OR function is also the same as a bit by bit sum with no carries.
Logical sums are often used to form checksums and error-detecting or error-cor
recting codes.
MASK has 1 bits in the positions to be complemented and 0 bits in the positions
that are to be left unchanged. For example,
EOR #%01010001 COMPLEMENT BITS 0f 4, AND 6
or
Either of these instructions may, of course, affect the other bits in the memory
location. The final value of bit 0, however, will surely be 0 if it was originally 1
and 1 if it was originally 0.
These procedures are useful if the accumulator contains a decimal digit in nega
tive logic (e.g., the input from a typical ten-position rotary or thumbwheel
switch).
6. Complement Carry flag.
ROR A ;MOVE CARRY TO BIT 7 OF A
EOR #$FF ^-COMPLEMENT ALL OF A
ROL A ;MOVE COMPLEMENTED CARRY BACK
Other combinations such as ROL, EOR, ROR, or ROR, EOR, ASL will work just
as well. We could leave the accumulator intact by saving it in the stack originally
and restoring it afterward.
An alternative that does not affect the accumulator is
BCC SETCAR
CLC ;CLEAR CARRY IF IT WAS SET
BCC DONE
SETCAR SEC ;SET CARRY IF IT WAS CLEARED
DONE NOP
Shift Instructions
We need a copy of the sign bit for an arithmetic shift. Of course, we could use a
memory location for temporary storage instead of the index register.
2. Shift memory locations ADDR and ADDR + 1 (MSB in ADDR + 1) left
logically.
The key point here is that we must shift the more significant byte circularly (i.e.,
rotate it). The first 8-bit shift moves one bit (the least significant bit for a right
shift and the most significant bit for a left shift) to the Carry. The 8-bit rotate then
moves that bit from the Carry into the other half of the word.
A shorter but slower version that does not use the accumulator is
A shorter but slower version that does not use the accumulator is
Rotate Instructions
A rotate through or with Carry acts as if the data were arranged in a circle with
its least significant bit connected to its most significant bit through the Carry flag.
A rotate without Carry differs in that it acts as if the least significant bit of the data
were connected directly to the most significant bit.
Test Instructions
1. Test accumulator. Set flags according to the value in the accumulator with
out changing that value.
TAX ;MOVE AND SET FLAGS
or
The instructions AND #$FF or OR A #0 would also do the job without affecting
the Carry (CMP #0 sets the Carry flag).
2. Test index register. Set flags according to the value in an index register
without changing that value.
CPX #0 ;CHECK VALUE IN INDEX REGISTER
3. Test memory location. Set flags according to the value in memory location
ADDR without changing that value.
INC ADDR ;CHECK VALUE IN MEMORY LOCATION
DEC ADDR
4. Test a pair of memory locations. Set the Zero flag according to the value in
memory locations ADDR and ADDR+1.
LDA ADDR ;TEST 16-BIT NUMBER FOR ZERO
ORA ADDR+1
This sequence sets the Zero flag to 1 if and only if both bytes of the 16-bit number
are 0. This procedure can readily be extended to handle numbers of any length.
MASK has a 1 bit in the position to be tested and 0 bits elsewhere. The instruc
tion sets the Zero flag to 1 if the tested bit position contains 0 and to 0 if the tested
bit position contains 1. For example,
6. Compare memory location ADDR with accumulator bit by bit. Set each
each bit position that is different.
EOR ADDR ;BIT-BY-BIT COMPARISON
In this group, we consider load, store, move, exchange, clear, and set instruc
tions.
96 6502 ASSEMBLY LANGUAGE SUBROUTINES
Load Instructions
The only instruction that has true indirect addressing is JMP. However, you can
produce ordinary indirect addressing by using the postindexed (indirect indexed)
addressing mode with index register Y set to 0.
An alternative approach is to clear index register X and use preindexing.
LDX #0 ;AVOID INDEXING
LDA (PGZRO,X) ;LOAD INDEXED INDIRECT
The advantage of the first approach is that one can index from the indirect
address with Y. For example, we could load addresses POINTL and POINTH
indirectly from the address in memory locations PGZRO and PGZRO+1 as
follows:
LDY #0 ;AVOID INDEXING
LDA (PGZRO),Y ;GET LSB OF ADDRESS INDIRECTLY
STA POINTL
INY ;GET MSB OF ADDRESS INDIRECTLY
LDA (PGZRO),Y
STA POINTH
Only the accumulator can be loaded using the indirect modes, but its contents can
be transferred easily to an index register.
This procedure allows the user of a computer system to initialize the status
register for debugging or testing purposes.
If you are restoring values from the stack, you must restore X and Y before A,
since there is no direct path from the stack to X or Y.
9. Load memory locations PGZRO and PGZRO+1 (a pointer on page 0) with
ADDR (ADDRH more significant byte, ADDRL less significant byte).
LDA #ADDRL ;INITIALIZE LSB
STA PGZRO
LDA #ADDRH INITIALIZE MSB
STA PGZRO+1
There is no simple way to initialize the indirect addresses that must be saved on
page 0.
Store Instructions
#0 ;AVOID INDEXING
STA (PGZRO,X) ;STORE INDEXED INDIRECT
If you are saving values in the stack, you must save A before X or Y, since there
is no direct path from X or Y to the stack.
Move Instructions
Note that JMP with indirect addressing loads the program counter with the con
tents of memory locations ADDR and ADDR+1; it acts more like LDA with
direct addressing than like LDA with indirect (indexed) addressing.
7. Block move. Transfer data from addresses starting at the one in memory
locations SORCE and SORCE+1 (on page 0) to addresses starting at the one in
memory locations DEST and DEST+1 (on page 0). Register Y contains the
number of bytes to be transferred.
MOVBYT DEY ;TEST NUMBER OF BYTES
LDA (SORCE),Y ;GET A BYTE FROM SOURCE
STA (DEST),Y ;MOVE TO DESTINATION
TYA
BNE MOVBYT
We assume here that the addresses do not overlap and that the initial value of Y is
1 or greater. Chapter 5 contains a more general block move.
The program becomes simpler if we reduce the base addresses by 1. That is, let
memory locations SORCE and SORCE+1 contain an address one less than the
lowest address in the source area, and let memory locations DEST and DEST+1
contain an address one less than the lowest address in the destination area. Now
we can exit when Y is decremented to 0.
MOVBYT LDA (SORCE)fY ;GET A BYTE FROM SOURCE
STA (DEST),Y ;MOVE BYTE TO DESTINATION
DEY
BNE MOVBYT ;COUNT BYTES
Exchange Instructions
or
TXA ; SAVE X
PHA
TYA ;Y TO X
TAX
PLA ;X TO Y
TAY
Both versions take the same number of bytes (assuming TEMP is on page 0). The
second version is slower but reentrant.
Clear Instructions
LDA #0
The 6502 treats 0 like any other number. There are no special clear instructions.
LDX #0
or
LDY #0
MASK has 0 bits in the positions to be cleared and 1 bits in the positions that are
to be left unchanged. For example,
Set Instructions
LDX #$FF
or
LDY #$FF
LDX #$FF
TXS
LDA #$FF
STA ADDR
MASK has 1 bits in the positions to be set and 0 bits elsewhere. For example,
ORA #%10000000 ;SET BIT 7 (SIGN BIT)
or
LDA #0
BEQ DEST
or
LDA #1
BNE DEST
RTS is just an ordinary indirect jump in which the processor obtains the destina
tion from the top of the stack. Be careful, however, of the fact that the processor
adds 1 to the address before proceeding.
3. Jump indexed, assuming that the base of the address table is BASE and the
index is in memory location INDEX. The addresses are arranged in the usual
6502 manner with the less significant byte first.5
• Using indirect addressing:
LDA INDEX
ASL A ;DOUBLE INDEX FOR 2-BYTE ENTRIES
TAX
LDA BASE,X ;GET LSB OF DESTINATION
STA INDIR
INX
LDA BASE,X ;GET MSB OF DESTINATION
STA INDIR+1
JMP (INDIR) ;JUMP INDIRECT TO DESTINATION
The second approach is faster but less straightforward. Note the following:
1. You must store the more significant byte first since the stack is growing
toward lower addresses. Thus the bytes end up in their usual order.
2. Since RTS adds 1 to the program counter after loading it from the stack, the
table entries must all be 1 less than the actual destination addresses for this
method to work correctly.
3. Documentation is essential, since this method uses RTS for the rather
surprising purpose of transferring control to a subroutine, rather than from it.
The mnemonic may confuse the reader, but it obviously does not bother the
microprocessor.
1. Branch if zero.
• Branch if accumulator contains zero.
TAX ;TEST ACCUMULATOR
BEQ DEST
or
Either AND #$FF or ORA #0 will set the Zero flag if (A) =0 without affecting
the Carry flag (CMP#0 sets Carry).
• Branch if an index register contains 0.
The instruction TXA or the sequence INX, DEX can be used to test the contents
of index register X without affecting the Carry flag (CPX#0 sets the Carry).
TXA, of course, changes the accumulator.
• Branch if a memory location contains 0.
or
MASK has a 1 bit in the position to be tested and 0s elsewhere. Note the inver
sion here; if the bit of the accumulator is a 0, the result is 0 and the Zero flag is set
to 1. Special cases are
Bit position 7
ASL A ;MOVE BIT 7 TO CARRY
BCC DEST
Bit position 6
ASL A ;MOVE BIT 6 TO NEGATIVE FLAG
BPL DEST
Bit position 0
LSR A ;MOVE BIT 0 TO CARRY
BCC DEST
MASK has a 1 bit in the position to be tested and 0s elsewhere. Special cases are
Bit position 7
Bit position 6
BIT ADDR ;TEST MEMORY
BVC DEST ;BRANCH ON BIT 6
The BIT instruction sets the Negative flag from bit 7 of the memory location and
the Overflow flag from bit 6, regardless of the contents of the accumulator.
We can also use the shift instructions to test the bits at the ends, as long as we
can tolerate changes in the memory locations.
Bit position 7
ASL ADDR ;TEST BIT 7
BCC DEST
Bit position 6
ASL ADDR ;TEST BIT 6
BPL DEST
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 1 05
Bit position 0
LSR ADDR ;TEST BIT 0
BCC DEST
2. Branch if not 0.
• Branch if accumulator does not contain 0.
TAX ;TEST ACCUMULATOR
BNE DEST
or
or
MASK has a 1 bit in the position to be tested and Os elsewhere. Note the inver
sion here; if the bit of the accumulator is a 1, the result is not 0 and the Zero flag
is set to 0. Special cases are
Bit position 7
Bit position 6
ASL A ;MOVE BIT 6 TO SIGN
BMI DEST ;AND TEST SIGN
Bit position 0
MASK has a 1 bit in the position to be tested and Os elsewhere. Special cases are
Bit position 7
Bit position 6
BIT ADDR ;TEST BIT 6 OF MEMORY
BVS DEST
The BIT instruction sets the Negative flag from bit 7 of the memory location and
the Overflow flag from bit 6, regardless of the contents of the accumulator.
We can also use the shift instructions to test the bits at the ends, as long as we
can tolerate changes in the memory locations.
Bit position 7
ASL ADDR ;TEST BIT 7 OF MEMORY
BCS DEST
This alternative is slower than BIT by 2 clock cycles, since it must write the result
back into memory.
Bit position 6
ASL ADDR ;TEST BIT 6 OF MEMORY
BMI DEST
Bit position 0
LSR ADDR ;TEST BIT 0 OF MEMORY
BCS DEST
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 1 07
3. Branch if Equal.
• Branch if (A) = VALUE.
CMP #VALUE ;COMPARE BY SUBTRACTING
BEQ DEST
Branch if (X) = FF
INX
BEQ DEST
Note: Neither of the next two sequences should be used to test for stack over
flow or underflow, since intervening instructions (for example, a single JSR or
RTS) could change the stack pointer by more than 1.
• Branch if (S) = VALUE.
TSX ;CHECK IF STACK IS AT LIMIT
CPX #VALUE
BEQ DEST
. Branch if (X) ?^ FF
INX
BNE DEST
• Branch if the contents of memory locations PGZRO and PGZRO+1 are not
equal to VAL16 (VAL16L less significant byte, VAL16M more significant byte).
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 1 09
• Branch if the contents of memory locations PGZRO and PGZRO + 1 are not
equal to those of memory locations LIML and LIMH.
LDA PGZRO+1 ;C0MPARE MSB'S
CMP LIMH
BNE DEST
LDA PGZRO ;C0MPARE LSB'S ONLY IF NECESSARY
CMP LIML
BNE DEST
Note: Neither of the next two sequences should be used to test for stack over
flow or underflow, since intervening instructions (for example, a single JSR or
RTS) could change the stack pointer by more than 1.
• Branch if (S) ^ VALUE.
TSX ;CHECK IF STACK IS AT LIMIT
CPX #VALUE
BNE DEST
5. Branch if Positive.
• Branch if contents of accumulator are positive.
TAX ;TEST ACCUMULATOR
BPL DEST
or
CMP #0 ;TEST ACCUMULATOR
BPL DEST
or
BIT ADDR
BPL DEST
110 6502 ASSEMBLY LANGUAGE SUBROUTINES
Remember that BIT sets the Negative flag from bit 7 of the memory location,
regardless of the contents of the accumulator.
6. Branch if Negative.
• Branch if contents of accumulator are negative.
TAX ;TEST ACCUMULATOR
BMI DEST
or
or
or
Remember that BIT sets the Negative flag from bit 7 of the memory location,
regardless of the contents of the accumulator.
7. Branch if Greater Than (Signed).
•Branch if (A) > VALUE.
CMP #VALUE ;COMPARE BY SUBTRACTING
BEQ DONE ;NO BRANCH IF EQUAL
BVS CHKOPP ;DID OVERFLOW OCCUR?
BPL DEST ;NO, THEN BRANCH ON POSITIVE
BMI DONE
CHKOPP BMI DEST ;YES, THEN BRANCH ON NEGATIVE
DONE NOP
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 111
The idea here is to branch if the result is greater than zero and overflow did not
occur, or if the result is less than zero and overflow did occur. Overflow makes
the apparent sign the opposite of the real sign.
• Branch if (A) > (ADDR).
CMP ADDR ;COMPARE BY SUBTRACTING
BEG DONE ;NO BRANCH IF EQUAL
BVS CHKOPP ;DID OVERFLOW OCCUR?
BPL DEST ;NO, THEN BRANCH ON POSITIVE
BMI DONE
CHKOPP BMI DEST ;YES, THEN BRANCH ON NEGATIVE
DONE NOP
The idea here is to branch if the result is greater than or equal to 0 and overflow
did not occur, or if the result is less than 0 and overflow did occur.
• Branch if (A) >(ADDR).
CMP ADDR ;COMPARE BY SUBTRACTING
BVS CHKOPP ;DID OVERFLOW OCCUR?
BPL DEST ;NO, THEN BRANCH ON POSITIVE
BMI DONE
CHKOPP BMI DEST ;YES, THEN BRANCH ON NEGATIVE
DONE NOP
The idea here is to branch if the result is negative and overflow did not occur, or if
the result is positive but overflow did occur.
• Branch if (A) < (ADDR) (signed).
CMP ADDR ;COMPARE BY SUBTRACTING
BVS CHKOPP ;DID OVERFLOW OCCUR?
BMI DEST ;NO, THEN BRANCH ON NEGATIVE
BPL DONE
CHKOPP BPL DEST ;YES, THEN BRANCH ON POSITIVE
DONE NOP
112 6502 ASSEMBLY LANGUAGE SUBROUTINES
The idea here is to branch if the result is 0, negative without overflow, or positive
with overflow.
• Branch if (A) < (ADDR) (signed).
CMP ADDR ;COMPARE BY SUBTRACTING
BEQ DEST ;BRANCH IF EQUAL
BVS CHKOPP ;DID OVERFLOW OCCUR?
BMI DEST ;NO, THEN BRANCH ON NEGATIVE
BPL DONE
CHKOPP BPL DEST ;YES, THEN BRANCH ON POSITIVE
DONE NOP
11. Branch if Higher (Unsigned). That is, branch if the unsigned comparison
is nonzero and does not require a borrow.
• Branch if (A) > VALUE (unsigned).
CMP #VALUE ;COMPARE BY SUBTRACTING
BEQ DONE ;NO BRANCH IF EQUAL
BCS DEST ;BRANCH IF NO BORROW NEEDED
DONE NOP
or
or
If the two values are the same, CMP sets the Carry to indicate that no borrow was
necessary.
or
or
or
13. Branch if Lower (Unsigned). That is, branch if the unsigned comparison
requires a borrow.
• Branch if (A) < (unsigned).
CMP #VALUE ;COMPARE BY SUBTRACTING
BCC DEST ;BRANCH IF BORROW GENERATED
14. Branch if Not Lower (Unsigned). That is, branch if the unsigned com
parison does not require a borrow.
• Branch if (A) > VALUE (unsigned).
CMP #VALUE ;COMPARE BY SUBTRACTING
BCS DEST ;BRANCH IF NO BORROW GENERATED
The Carry flag is set to one if the subtraction does not generate a borrow.
SKIP INSTRUCTIONS
where TRANS is the subroutine that actually transfers control using a jump
instruction. Note that TRANS ends with a jump, not with a return. Typical
TRANS routines are:
LDA INDEX
ASL A ;DOUBLE INDEX FOR 2-BYTE ENTRIES
TAX
LDA BASE,X ;GET LSB OF DESTINATION
STA INDIR
INX
LDA BASE,X ;GET MSB OF DESTINATION
STA INDIR+1
JMP (INDIR) ;JUMP INDIRECT TO DESTINATION
or
LDA INDEX
ASL A ;DOUBLE INDEX FOR 2-BYTE ENTRIES
TAX
LDA BASE+1,X ;GET MSB OF DESTINATION
PHA
LDA BASE,X ;GET LSB OF DESTINATION
PHA
RTS ;JUMP TO DESTINATION PLUS 1
In the second approach, the table must contain the actual destination addresses
minus 1, since RTS adds 1 to the program counter after loading it from the stack.
RETURN INSTRUCTIONS
The RTS instruction returns control automatically to the address saved at the
top of the stack (plus 1). If the return address is saved elsewhere (i.e., in two
memory locations), you can return control to it by performing an indirect jump.
Note that you must add 1 to the return address to simulate RTS.
The following sequence pops the return address from the top of the stack, adds
1 to it, and stores the adjusted value in memory locations RETADR and
RETADR + 1.
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 119
A final JMP (RETADR) will now transfer control to the proper place.
• Return control to the address at the top of the stack after it has been incre
mented by an offset NUM. This sequence allows you to transfer control past
parameters, data, or other nonexecutable items.
or
• Change the return address to RETPT. Assume that the return address is
stored currently at the top of the stack. RETPT consists of RETPTH (MSB) and
RETPTL (LSB).
120 6502 ASSEMBLY LANGUAGE SUBROUTINES
TSX
LDA #RETPTL
STA $0101,X
LDA #RETPT
STA $0102fX
RTS
If the initial portion of the interrupt service routine saves all the registers with
the sequence.
MISCELLANEOUS INSTRUCTIONS
In this category, we include push and pop instructions, halt, wait, break,
decimal adjust, enabling and disabling of interrupts, translation (table lookup),
and other instructions that do not fall into any of the earlier categories.
1. Push Instructions.
• Push index register X.
Since the stack is growing toward lower addresses, the 16-bit number ends up
stored in its usual 6502 form.
We assume that the 16-bit number is stored in the usual 6502 form with the less
significant byte at the lower address.
Wait Instructions
The processor will continue executing the instruction until it is interrupted and
will resume executing it after the interrupt service routine returns control. Of
course, maskable interrupts must have been enabled or the processor will
1 22 6502 ASSEMBLY LANGUAGE SUBROUTINES
execute the loop endlessly. The nonmaskable interrupt can interrupt the pro
cessor at any time.
Another alternative is a sequence that waits for a high-to-low transition on the
Set Overflow input. Such a transition sets the Overflow (V) flag. So the required
sequence is
Adjust Instructions
4. Enter decimal mode but save the old Decimal Mode flag.
PHP ;SAVE OLD DECIMAL MODE FLAG
SED ;ENTER DECIMAL MODE
A final PLP instruction will restore the old value of the Decimal Mode flag (and
the rest of the status register as well).
5. Enter binary mode but save the old Decimal Mode flag.
A final PLP instruction will restore the old value of the Decimal Mode flag (and
the rest of the status register as well).
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 1 23
After a sequence that must run with interrupts enabled, a PLP instruction will
restore the previous state of the interrupt system (and the rest of the status
register as well).
2. Disable interrupts but save previous value of I flag.
PHP ;SAVE OLD I FLAG
SEI ;DISABLE INTERRUPTS
After a sequence that must run with interrupts disabled, a PLP instruction will
restore the previous state of the interrupt system (and the rest of the status
register as well).
Translate Instructions
This procedure can be used to convert data from one code to another.
• Indirect Addressing. You can provide indirect addressing on the 6502 pro
cessor (for addresses on page 0) by using the postindexed (indirect indexed)
1 24 6502 ASSEMBLY LANGUAGE SUBROUTINES
In the case of instructions that lack the indirect indexed mode (such as ASL,
DEC, INC, LSR, ROL, ROR), you must move the data to the accumulator, oper
ate on it there, and then store it back in memory.
3. Increment the data at the address in memory locations PGZRO and
PGZRO+1.
4. Logically shift right the data at the address in memory locations PGZRO
and PGZRO + 1.
LDY #0 ;SET INDEX TO ZERO
LDA (PGZRO),Y ;GET THE DATA
LSR A ;SHIFT IT RIGHT
STA (PGZRO),Y ;STORE THE RESULT BACK
The only way to provide indirect addressing for other pages is to move the
indirect address to page 0 first.
6. Clear the address in memory locations INDIR and INDIR+1 (not on
page 0).
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 1 25
PHA ;SAVE A
AND BASE,X ;LOGICAL AND INDEXED
The Zero flag is set as if an indexed BIT had been executed and the contents of A
are available at the top of the stack.
2. CPX or CPY
CPX or CPY indexed can be simulated by moving the index register to A and
using CMP. That is, CPX indexed with Y can be simulated by the sequence:
TXA ;MOVE X TO A
CMP BASE,Y ;THEN COMPARE INDEXED
3. JMP
JMP indexed can be simulated by calculating the required indexed address,
storing it in memory, and using either JMP indirect or RTS to transfer control.
The sequences are:
LDA INDEX
ASL A ;DOUBLE INDEX FOR 2-BYTE ENTRIES
TAX
LDA BASE,X ;GET LSB OF DESTINATION
STA INDIR
INX
LDA BASE,X ;GET MSB OF DESTINATION
STA INDIR+1
JMP (INDIR) ;JUMP INDIRECT TO DESTINATION
126 6502 ASSEMBLY LANGUAGE SUBROUTINES
or
LDA INDEX
ASL A ;DOUBLE INDEX FOR 2-BYTE ENTRIES
TAX
LDA BASE+1,X ;GET MSB OF DESTINATION
PHA
LDA BASEfX ;GET LSB OF DESTINATION
PHA
RTS ;JUMP INDIRECT TO DESTINATION OFFSET 1
The second approach requires that the table contain entries that are all 1 less than
the actual destinations, since RTS adds 1 to the program counter after restoring it
from the stack.
4. JSR
JSR indexed can be simulated by calling a transfer program that executes JMP
indexed as shown above. The ultimate return address remains at the top of the
stack and a final RTS instruction will transfer control back to the original calling
program. That is, the main program contains:
JSR TRANS
TRANS performs an indexed jump and thus transfers control to the actual
subroutine.
5. STX or STY
STX or STY indexed can be simulated by moving the index register to A and
using STA. That is, we can simulate STX indexed with Y by using the sequence:
TXA ;MOVE X TO A
STA BASE,Y ;THEN STORE INDEXED
TEMP and TEMP+1 now contain a base address that can be used (in conjunction
with INDEX) in the indirect indexed mode.
CHAPTER 2: IMPLEMENTING ADDITIONAL INSTRUCTIONS AND ADDRESSING MODES 1 27
Examples
1. Load accumulator indexed.
LDY INDEX ;GET LSB OF INDEX
LDA (TEMP),Y ;LOAD A INDIRECT INDEXED
2. Store accumulator indexed, assuming that we have saved A at the top of the
stack.
LDY INDEX ;GET LSB OF INDEX
PLA ;RESTORE A
STA (TEMP),Y ;STORE A INDIRECT INDEXED
Examples
• Load the accumulator from address BASE using autopreincrementing on
index register X.
INX ;AUTOPREINCREMENT X
LDA BASE,X
Examples
• Load the accumulator from address BASE using autopostincrementing on
index register Y.
LDA BASE,Y ;AUTOPOSTINCREMENT Y
INY
• Load the accumulator from the address in memory locations PGZRO and
PGZRO + 1 using autopostincrementing on the contents of memory locations
INDEX and INDEX + 1.
Examples
DEX ;AUTOPREDECREMENT X
LDA BASE,X
Examples
• Load the accumulator from address BASE using autopostdecrementing on
index register Y.
• Load the accumulator from the address in memory locations PGZRO and
PGZRO + 1 using autopostdecrementing on the contents of memory locations
INDEX and INDEX 4- 1.
Examples
1. Rotate right the data at the preindexed address obtained by indexing with X
from base address PGZRO.
LDA (PGZRO,X) ;GET THE DATA
ROR A ;ROTATE DATA RIGHT
STA (PGZROrX) ;STORE RESULT BACK IN MEMORY
Examples
1. Decrement the data at the address in memory locations PGZRO and
PGZRO+1 using Y as an index.
2. Rotate left the data at the address in memory locations PGZRO and
PGZRO+1 using Y as an index.
REFERENCES
• To provide the beginner with a starting point in the difficult process of locat
ing and correcting errors.
CATEGORIZATION OF
PROGRAMMING ERRORS
We may generally divide common 6502 programming errors into the following
categories:
• Using the Carry improperly. Typical errors include forgetting to clear the
Carry before addition or set it before subtraction, and interpreting it incorrectly
after comparisons (it acts as an inverted borrow).
133
1 34 6502 ASSEMBLY LANGUAGE SUBROUTINES
• Using the other flags improperly. Typical errors include using the wrong flag
(such as Negative instead of Carry), branching after instructions that do not
affect a particular flag, inverting the branch conditions (particularly when the
Zero flag is involved), and changing a flag accidentally before branching.
• Confusing addresses and data. Typical errors include using immediate
instead of direct addressing, or vice versa, and confusing memory locations on
page 0 with the addresses accessed indirectly through those locations.
• Using the wrong formats. Typical errors include using BCD (decimal)
instead of binary, or vice versa, and using binary or hexadecimal instead of
ASCII.
• Failing to provide proper initial conditions for routines or for the microcom
puter as a whole. Most routines require the initialization of counters, indirect
addresses, indexes, registers, flags, and temporary storage locations. The
microcomputer as a whole requires the initialization of the Interrupt Disable and
Decimal Mode flags and all global RAM addresses (note particularly indirect
addresses and other temporary storage on page 0).
• Organizing the program incorrectly. Typical errors include skipping or
repeating initialization routines, failing to update indexes, counters, or indirect
addresses, and forgetting to save intermediate or final results.
A common source of errors, one that is beyond the scope of our discussion, is
conflict between user programs and systems programs. A simple example is a
user program that saves results in temporary storage locations that operating
systems or utility programs need for their own purposes. The results thus disap
pear mysteriously even though a detailed trace of the user program does not
reveal any errors.
More complex sources of conflict may include the interrupt system, input/out
put ports, the stack, or the flags. After all, the systems programs must employ the
same resources as the user programs. (Systems programs generally attempt to
save and restore the user's environment, but they often have subtle or unex
pected effects.) Making an operating system transparent to the user is a problem
comparable to devising a set of regulations, laws, or tax codes that have no
loopholes or side effects.
CHAPTER 3: COMMON PROGRAMMING ERRORS 135
The following instructions and conventions are the most common sources of
errors:
• CMP, CPX, and CPY affect the Carry as if it were an inverted borrow, that
is, they set the Carry if the subtraction of the memory location from the register
did not require a borrow, and they clear the Carry if it did. Thus, Carry = 1 if no
borrow was necessary and Carry = 0 if a borrow was required. This is contrary to
the sense of the Carry in most other microprocessors (the 6800, 6809, 8080,
8085, orZ-80).
• SBC subtracts the inverted Carry flag from the normal subtraction of the
memory location from the accumulator. That is, it produces the result (A) —
(M) — (1 — Carry). If you do not want the Carry flag to affect the result, you
must set it with SEC. Like comparisons, SBC affects the Carry as if it were an
inverted borrow; Carry = 0 if the subtraction requires a borrow and 1 if it does
not.
• ADC always includes the Carry in the addition. This produces the result (A)
= (A) + (M) + Carry. If you do not want the Carry flag to affect the result, you
must clear it with CLC. Note that the Carry has its normal meaning after ADC.
Examples
1. CMP ADDR
This instruction sets the flags as if the contents of memory location ADDR had
been subtracted from the accumulator. The Carry flag is set if the subtraction
does not require a borrow and cleared if it does. Thus
We are assuming that both numbers are unsigned. Note that the Carry is set (to
1) if the numbers are equal.
2. SBC #VALUE
This instruction subtracts VALUE and 1 —Carry from the accumulator. It sets
the flags just like a comparison. To subtract VALUE alone from the accumulator,
you must use the sequence
This sequence produces the result (A) = (A) - VALUE. If VALUE = 1, the
sequence is equivalent to a Decrement Accumulator instruction (remember,
DEC cannot be applied to A).
136 6502 ASSEMBLY LANGUAGE SUBROUTINES
3. ADC #VALUE
This instruction adds VALUE and Carry to the accumulator. To add VALUE
alone to the accumulator, you must use the sequence
CLC ;CLEAR CARRY
ADC #VALUE ;ADD VALUE
This sequence produces the result (A) = (A) + VALUE. If VALUE = 1, the
sequence is equivalent to an Increment Accumulator instruction (remember,
INC cannot be applied to A).
Instructions for the 6502 generally have expected effects on the flags. The only
special case is BIT. Situations that require some care include the following:
• Store instructions (STA, STX, and STY) do not affect the flags, so the flags
do not necessarily reflect the value that was just stored. You may need to test the
register by transferring it to another register or comparing it with 0. Note that load
instructions (including PHA) and transfer instructions (excluding TXS) affect
the Zero and Negative flags.
• After a comparison (CMP, CPX, or CPY), the Zero flag indicates whether
the operands are equal. The Zero flag is set if the operands are equal and cleared if
they are not. There is some potential confusion here — BEQ means branch if the
result is equal to 0; that is, branch if the Zero flag is 1. Be careful of the difference
between the result being 0 and the Zero flag being 0. These two conditions are
opposites; the Zero flag is 0 if the result is not 0.
contents are greater than or equal to the other operand. Note that comparing
equal operands clears the Negative flag. As with the Carry, you can handle the
equality case in the opposite way by adjusting either operand or by reversing the
subtraction.
BCC NEXT
INC OVFLW
NEXT NOP
The branch condition is the opposite of the condition under which the section
should be executed.
• Increment and decrement instructions do not affect the Carry flag. This
allows the instructions to be used for counting in loops that perform multiple-
byte arithmetic (the Carry is needed to transfer carries or borrows between
bytes). Increment and decrement instructions do, however, affect the Zero and
Negative flags; you can use the effect on the Zero flag to determine whether an
increment has produced a carry. Note the following typical sequences:
We determine if a carry has been generated by examining the Zero flag after
incrementing the less significant byte.
• The BIT instruction has rather unusual effects on the flags. It places bit 6 of
the memory location in the Overflow flag and bit 7 in the Negative flag, regard
less of the value in the accumulator. Thus, only the Zero flag actually reflects the
logical ANDing of the accumulator and the memory location.
1 38 6502 ASSEMBLY LANGUAGE SUBROUTINES
• Only a few instructions affect the Carry or Overflow flags. The instructions
that affect Carry are arithmetic (ADC, SBC), comparisons (CMP, CPX, and
CPY), and shifts (ASL, LSR, ROL, and ROR), besides the obvious CLC and
SEC. The only instructions that affect Overflow are ADC, BIT, CLV, and SBC;
comparison and shift instructions do not affect the Overflow flag, unlike the
situation in the closely related 6800 and 6809 microprocessors.
Examples
1. The sequence
STA $1700
BEQ DONE
will have unpredictable results, since STA does not affect any flags. Sequences
that will produce a jump if the value stored is 0 are
STA $1700
CMP #0 ;TEST ACCUMULATOR
BEQ DONE
or
STA $1700
TAX ;TEST ACCUMULATOR
BEQ DONE
Thus, if you want to increment memory location COUNT, if (A) = 2516, use the
sequence
Note that we use BNE to branch around the increment if the condition (A =
2516) does not hold. It is obviously easy to err by inverting the branch condition.
3. The instruction CPX #$25 sets the Carry flag as follows:
Carry = 0 if the contents of X are between 00 and 2416
Carry = 1 if the contents of X are between 2516 and FF16
Thus, the Carry flag is cleared if X contains an unsigned number less than the
other operand and set if X contains an unsigned number greater than or equal to
the other operand.
CHAPTER 3: COMMON PROGRAMMING ERRORS 1 39
If you want to clear the Carry if the X register contains 2516, use CPX #$26
instead of CPX #$25. That is, we have
CPX #$25
BCC LESS ;BRANCH IF (X) LESS THAN 25
or
CPX #$26
BCC LESSEQ ;BRANCH IF (X) 25 OR LESS
4. The sequence SEC, SBC #$40 sets the Negative (Sign) flag as follows:
Note that we cannot use CMP here, since it does not affect the Overflow flag. We
could, however, use the sequence
5. The sequence
INC ADDR
BCS NXTPG
will have unpredictable results, since INC does not affect the Carry flag. A
sequence that will produce a jump, if the result of the increment is 00 (thus
implying the production of a carry), is illustrated below.
1 40 6502 ASSEMBLY LANGUAGE SUBROUTINES
INC ADDR
BEQ NXTPG
We can tell when an increment has produced a carry, but we cannot tell when a
decrement has required a borrow since the result then is FFl6, not 0. Thus, it is
much simpler to increment a multibyte number than to decrement it.
6. The sequence
BIT ADDR
BVS DEST
BIT ADDR
BPL DEST
LDA #MASK
BIT ADDR
This sequence sets the Zero flag if logically ANDing MASK and the contents of
ADDR produces a result of 0. A typical example using the Zero flag is
LDA #%00010000
BIT ADDR
BNE DEST ;BRANCH IF BIT 4 OF ADDR IS 1
This sequence forces a branch if the result of the logical AND is nonzero, that is,
if bit 4 of ADDR is 1.
The effects of BIT on the Overflow and Negative flags do not generally cause
programming errors since there are no standard, widely used effects that might
cause confusion. These effects do, however, create documentation problems
since the approach is unique and those unfamiliar with the 6502 cannot be
expected to guess what is happening.
7. The sequence
CMP #VALUE
BVS DEST
produces unpredictable results, since CMP does not affect the Overflow flag.
Instead, to produce a branch if the subtraction results in two's complement over
flow, use the sequence
SEC ;SET INVERTED BORROW
SBC #VALUE ;SUBTRACT VALUE
BVS DEST ;BRANCH IF OVERFLOW OCCURS
CHAPTER 3: COMMON PROGRAMMING ERRORS 141
• The immediate addressing mode requires the actual data as an operand. That
is, LDA #$40 loads the accumulator with the number 4016.
. The absolute and zero page (direct) addressing modes require the address of
the data as an operand. That is, LDA $40 loads the accumulator with the contents
of memory location 004016.
• The indirect indexed and indexed indirect addressing modes obtain the
indirect address from two memory locations on page 0. The indirect address is in
two memory locations starting at the specified address; it is stored upside-down,
with its less significant byte at the lower address. Fortunately, the indexed
indirect (preindexed) mode is rarely used and is seldom a cause of errors. The
meaning of addressing modes with JMP and JSR can be confusing, since these
instructions use addresses as if they were data. The assumption is that one could
not transfer control to a number, so a jump with immediate addressing would be
meaningless. However, the instruction JMP $1C8O loads 1C8O16 into the program
counter, just like a load with immediate addressing, even though we conven
tionally say that the instruction uses absolute addressing. Similarly, the instruc
tion JMP (ADDR) loads the program counter with the address from memory
locations ADDR and ADDR+1; it thus acts like a load instruction with absolute
(direct) addressing.
Examples
1. LDX#$20 loads the number 2016 into index register X. LDX $20 loads the
contents of memory location 002016 into index register X.
2. LDA ($40) ,Y loads the accumulator from the address obtained by indexing
with Y from the base address in memory locations 004016 and 004116 (MSB in
004116). Note that if LDA ($40),Y makes sense, then LDA ($41),Y generally
does not, since it uses the base address in memory locations 004116 and 004216.
Thus, the indirect addressing modes generally make sense only if the indirect
addresses are aligned properly on word boundaries; however, the 6502 does not
check this alignment in the way that many computers (particularly IBM
machines) do. The programmer must make sure that all memory locations used
indirectly contain addresses with the bytes arranged properly.
• The upper and lower thresholds against which the result is to be compared.
• The address of the next block in the queue.
Thus, the block contains data, direct addresses, and indirect addresses. Typical
errors that a programmer could make are
• Assuming that the next block starts within the current block, rather than at
the address given in the current block.
Jump tables are another common source of errors. The following are alterna
tive implementations:
• Form a table ofjump instructions and transfer control to the correct element
(for example, to the third jump instruction).
• Form a table of destination addresses and transfer control to the contents of
the correct element (for example, to the address in the third element).
You will surely have problems if you try to use the jump instructions as
indirect addresses or if you try to execute the indirect addresses.
FORMAT ERRORS
• The default mode of most assemblers is decimal; that is, most assemblers
assume all numbers to be decimal unless they are specifically designated as some
thing else. A few assemblers (such as Apple's miniassembler and the mnemonic
entry mode in Rockwell's AIM-65) assume hexadecimal as a default.
• ADC and SBC instructions produce decimal results if the Decimal Mode flag
is 1 and binary results if the Decimal Mode flag is 0. All other instructions,
including DEC, DEX, DEY, INC, INX, and INY, always produce binary results.
CHAPTER 3: COMMON PROGRAMMING ERRORS 143
You should make special efforts to avoid the following common errors:
Examples
1. LDA 2000
This instruction loads the accumulator from memory address 200010 (07D016),
not address 200016. The assembler will not produce an error message, since 2000
is a valid decimal number.
2. AND #00000011
This instruction logically ANDs the accumulator with the decimal number 11
(10112), not with the binary number 11 (310). The assembler will not produce an
error message, since 00000011 is a valid decimal number despite its unusual
form.
3. ADC #40
This instruction adds 4010 (not 40,6 = 6410) and the Carry to the accumulator.
Note that 4010 is not the same as 40 BCD, which is 4016; 4010 = 2816. The assem
bler will not produce an error message, since 40 is a valid decimal number.
4. LDA #3
This instruction loads the accumulator with the number 3. If this value is now
sent to an ASCII output device, it will respond as if it had received the character
ETX (0316), not the character 3 (3316). The correct version is
LDA #f3 ;GET AN ASCII 3
LDA $40
STA PORT
1 44 6502 ASSEMBLY LANGUAGE SUBROUTINES
will not print that digit on an ASCII output device. The correct sequence is
or
6. If input port IPORT contains a single ASCII decimal digit, the sequence
LDA IPORT
STA $40
will not store the actual digit in memory location 0040l6. Instead, it will store the
ASCII version, which is the actual digit plus 30l6. The correct sequence is
or
Handling decimal arithmetic on the 6502 microprocessor is simple, since the pro
cessor has a Decimal Mode (D) flag. When that flag is set (by SED), all additions
and subtractions produce decimal results. So, the following sequences implement
decimal addition and subtraction:
LDA $40
CLC
ADC #1
STA $40
LDA $40
SEC
SBC #1
STA $40
The problem with the decimal mode is that it has implicit effects. That is, the
same ADC and SBC instructions with the same data will produce different
results, depending on the state of the Decimal Mode flag. The following pro
cedures will reduce the likelihood of the implicit effects causing unforeseen
errors:
• Initialize the Decimal Mode flag (with CLD) as part of the regular system
initialization. Note that RESET has no effect on the Decimal Mode flag.
• Clear the Decimal Mode flag as soon as you are through performing decimal
arithmetic.
• Initialize the Decimal Mode flag in interrupt service routines that include
ADC or SBC instructions. That is, such service routines should execute CLD
before performing any binary addition or subtraction.
• If you are counting an index register down to 0, the zero index value may
never be used. The solution is to reduce the base address or addresses by 1. For
example, if the terminating sequence in a loop is
DEX
BNE LOOP
LDX #NTIMES
LDA #0
CLEAR STA BASE-1,X
DEX
BNE CLEAR
146 6502 ASSEMBLY LANGUAGE SUBROUTINES
Note the use of BASE—1 in the indexed store instruction. The program clears
addresses BASE through BASE + NTIMES-1.
Here INDIR and INDIR+1 are the locations on page 0 that contain the indirect
address.
Example
1. Let us assume (INDIR) = 8016 and (INDIR+1) = 4C16, so that the initial
base address is 4C8016. If the loop refers to the address (INDIR), Y, the effective
address is (INDIR+1) (INDIR) + Y or 4C8016 + (Y). When Y = FF16, the
effective address is
The sequence shown above for incrementing the index and the indirect address
produces the results
(Y) = (Y) + 1 = 00
(INDIR+1) = (INDIR + 1) = 1 = 4D16
CHAPTER 3: COMMON PROGRAMMING ERRORS 1 47
IMPLICIT EFFECTS
• The changing of the Negative and Zero flags by load and transfer instruc
tions, such as LDA, LDX, LDY, PLA, TAX, TAY, TSX, TXA, and TYA.
• The dependence of the results of ADC and SBC instructions on the values of
the Carry and Decimal Mode flags.
• The special use of the Negative and Overflow flags by the BIT instruction.
The use of the memory address one larger than the specified one in the
indirect, indirect indexed, and indexed indirect addressing modes.
• The changing of the stack pointer by PHA, PHP, PLA, PLP, JSR, RTS, RTI,
and BRK. Note that JSR and RTS change the stack pointer by 2, and BRK and
RTI change it by 3.
• The saving of the return address minus 1 by JSR and the addition of 1 to the
restored address by RTS.
• The inclusion of the Carry in the rotate instructions ROL and ROR. The
rotation involves nine bits, not eight bits.
Examples
1. LDX $40
This instruction affects the Negative and Zero flags, so those flags will no
longer reflect the value in the accumulator or the result of the most recent opera
tion.
2. ADC #$20
This instruction adds in the Carry flag as well as the immediate data (2016). The
result will be binary if the Decimal Mode flag is cleared, but BCD if the Decimal
Mode flag is set.
3. BIT $1700
This instruction sets the Overflow flag from the value of bit 6 of memory loca
tion 170016. This is the only instruction that has a completely unexpected effect
on that flag.
1 48 6502 ASSEMBLY LANGUAGE SUBROUTINES
4. JMP ($1COO)
This instruction transfers control to the address in memory locations 1COO16
and 1CO116 (MSB in lC01l6). Note that lC01l6 is involved even though it is not
specified, since indirect addresses always occupy two bytes of memory.
5. PHA
This instruction not only saves the accumulator in memory, but it also decre
ments the stack pointer by 1.
6. RTS
This instruction not only loads the program counter from the top two locations
in the stack, but it also increments the stack pointer by 2 and the program counter
by 1.
7. ROR A
This instruction rotates the accumulator right 1 bit, moving the former con
tents of bit position 0 into the Carry and the former contents of the Carry into bit
position 7.
INITIALIZATION ERRORS
The initialization routines must perform the following tasks, either for the
microcomputer system as a whole or for particular routines:
• Load all RAM locations with initial values. This includes indirect addresses
and other temporary storage on page 0. You cannot assume that a memory loca
tion contains 0 just because you have not used it.
• Load all registers and flags with initial values. Reset initializes only the Inter
rupt Disable flag (to 1). Note, in particular, the need to initialize the Decimal
Mode flag (usually with CLD) and the stack pointer (using the LDX, TXS
sequence).
• Load all counters and indirect addresses with initial values. Be particularly
careful of addresses on page 0 that are used in either the indirect indexed (postin-
dexed) addressing mode or the indexed indirect (preindexed) mode.
that a register, flag, or memory location contains zero just because you have not
used it.
These errors are generally easy to correct. Often the only problem is an error,
such as omitting the semicolon or other delimiter in front of a comment, that
confuses the assembler and results in a series of meaningless error messages.
There are, however, many common errors that assemblers will not recognize.
The programmer should be aware that his or her program may contain such
errors even if the assembler does not report them. Typical examples are
• If you enter an invalid operand such as LDA #$HX. Some assemblers will
accept this and generate incorrect code.
The assembler will recognize only errors that its developer anticipated. Pro
grammers are often able to make mistakes that the developer never imagined,
much as automobile drivers are often capable of performing maneuvers that
never occurred in the wildest dreams of a highway designer or traffic planner.
Note that only a line-by-line hand checking of the program will find errors that
the assembler does not recognize.
IMPLEMENTATION ERRORS
Most errors in I/O drivers involve both hardware and software, so they are
often difficult to categorize. Some mistakes you should watch for are
• Confusing input ports and output ports. Many I/O interfaces use the READ/
WRITE line for addressing, so that reading and writing the same memory address
results in operations on different physical registers. Even when this is not done, it
may still be impossible to read back output data unless it is latched and buffered.
• Failing to keep copies of output data. Remember that you may not be able to
read the data back from the output port. If you need to repeat it later as part of
repeating a transmission that was incorrectly received, change part of it (turn on
or off one of several indicator lights attached to the same port), or save it as part
of the interrupted status (the data is the current priority level). You must save a
copy in memory. The copy must be updated every time the actual data is changed.
• Confusing actual I/O ports with registers that are inside I/O devices. Pro
grammable I/O devices, such as the 6520, 6522, 6551, and 6850, have control or
command registers which determine how the device operates, and status registers
that reflect the current state of the device or the transfer. These registers are
inside the I/O devices; they are not connected to peripherals. Transferring data to
or from status or control registers is not the same as transferring data to or from
actual I/O ports.
• Using bidirectional ports improperly. Many devices, such as the 6520, 6522,
6530, and 6532, have bidirectional I/O ports. The ports (and perhaps even
individual lines) can be used either as inputs or outputs. Normally, resetting the
computer to avoid initial transients makes these ports inputs, so you must
explicitly change them to outputs if necessary. Be cautious when reading bits or
ports that are designated as outputs or writing into bits or ports that are desig
nated as inputs. The only way to determine what will happen is to read the docu
mentation for the specific device.
• Forgetting to clear status after performing an I/O operation. Once the pro
cessor has read data from an input port, that port should revert to the not ready
state. Similarly, once the processor has written data into an output port, that port
should revert to the not ready state. Some I/O devices change the status of their
ports automatically after input or output operations, but others either do not or
(as in the 6520) change status automatically only after input operations. Leaving
the status set can result in an endless loop or highly erratic operation.
COMMON ERRORS IN
INTERRUPT SERVICE ROUTINES
Many interrupt-related errors involve both hardware and software, but some
of the common mistakes include the following:
• Failing to reenable interrupts during the service routine. The 6502 processor
automatically disables interrupts after accepting one. It does reenable interrupts
when RTI is executed, since RTI restores the status register from the stack.
• Failing to save and restore registers. The 6502 does not automatically save
any registers except the program counter and the status register. So the
accumulator, index registers, and scratchpad locations must be saved explicitly in
the stack.
• Failing to reenable the interrupt after a sequence that must run with inter
rupts disabled. A corollary problem here is that you do not want to enable inter
rupts if they were not enabled when the sequence was entered. The solution is to
save the previous state of the Interrupt Disable flag (using PHP) before execut
ing the sequence and restore the previous state (using PLP) afterward. Note,
however, that PLP restores the entire status register.
• Failing to initialize or establish the value of the Decimal Mode flag. An inter
rupt service routine should not assume a particular value (0) for the D flag.
Instead, it should initialize that flag with CLD or SED if it executes ADC or SBC
instructions. There is no need to save or restore the old D flag since that is done
automatically as part of the saving and restoring of the status register. Initializing
the D flag avoids problems if the service routine is entered from a program that
runs with the D flag set.
• Failing to clear the signal that caused the interrupt. The service routine must
clear the interrupt even if it does not require an immediate response or any input
or output operations. Even when the processor has, for example, no data to send
to an interrupting output device, it must still either clear the interrrupt or disable
it. Otherwise, the processor will get caught in an endless loop. Similarly, a real
time clock interrupt will typically require no servicing other than an updating of
time, but the service routine still must clear the clock interrupt. This clearing may
involve reading a 6520 or 6522 I/O port or timer.
• Failing to communicate with the main program. The main program will not
realize that the interrupt has been serviced unless it is informed explicitly. The
usual way to inform the main program is to have the interrupt service routine
change a flag that the main program can examine. The main program will then
know that the service routine has been executed. The procedure is comparable to
the practice of a postal patron raising a flag to indicate that he or she has mail to be
picked up. The postman lowers the flag after picking up the mail. Note that this
CHAPTER 3: COMMON PROGRAMMING ERRORS 1 55
simple procedure means that the main program must examine the flag often
enough to avoid missing data or messages. Of course, the programmer can always
provide an intermediate storage area (or buffer) that can hold many data items.
• Failing to save and restore priority. The priority of an interrupt is often held
in a write-only register or in a memory location. That priority must be saved just
like the registers and restored properly at the end of the service routine. If the
priority register is write-only, a copy of its contents must be saved in memory.
Introduction to the
Program Section
The program section contains sets of assembly language subroutines for the
6502 microprocessor. Each subroutine is documented with an introductory sec
tion and comments; each is followed by at least one example of its use. The
introductory material contains the following information:
3. Registers used
4. Execution time
5. Program size
7. Special cases
8. Entry conditions
9. Exit conditions
10. Examples
We have made each routine as general as possible. This is most difficult in the
case of the input/output (I/O) and interrupt service routines described in Chap
ters 10 and 11, since in practice these routines are always computer-dependent.
In such cases, we have limited the computer dependence to generalized input and
output handlers and interrupt managers. We have drawn specific examples there
from the popular Apple II computer, but the general principles are applicable to
other 6502-based computers as well.
In all routines, we have used the following parameter passing techniques:
157
158 6502 ASSEMBLY LANGUAGE SUBROUTINES
Where there has been a choice between execution time and memory usage, we
have chosen the approach that minimizes execution time. For example, in the
case of arrays that are more than 256 bytes long, it is faster to handle the full
pages, then handle the remaining partial page separately, than to handle the
entire array in a single loop. The reason is that the first approach can use an 8-bit
counter in an index register, whereas the second approach requires a 16-bit
counter in memory.
We have also chosen the approach that minimizes the number of repetitive
calculations. For example, in the case of array indexing, the number of bytes be
tween the starting addresses of elements differing only by one in a particular
subscript (known as the size of that subscript) depends only on the number of
bytes per element and the bounds of the array. Thus, the sizes of the various
subscripts can be calculated as soon as the bounds of the array are known; the
sizes are therefore used as parameters for the indexing routines, so that they need
not be calculated each time a particular array is indexed.
As for execution time, we have specified it for most short routines. For longer
routines, we have given an approximate execution time. The execution time of
programs involving many branches will obviously depend on which path is
followed in a particular case. This is further complicated for the 6502 by the fact
that branch instructions themselves require different numbers of clock cycles
depending on whether the branch is not taken, taken within the current page, or
taken across a page boundary. Thus, a precise execution time is often impossible
to define. The documentation always contains at least one typical example show
ing an approximate or maximum execution time.
Our philosophy on error indications and special cases has been the following:
1. Routines should provide an easily tested indicator (such as the Carry flag)
of whether any errors or exceptions have occurred.
Code Conversion
4A Binary to BCD Conversion 163
4B BCD to Binary Conversion 166
iC Binary to Hexadecimal ASCII Conversion 168
4D Hexadecimal ASCII to Binary Conversion 171
4E Conversion of a Binary Number to a String of ASCII Decimal Digits 174
4F Conversion of a String of ASCII Decimal Digits to a Binary Number 180
4G Lower-Case ASCII to Upper-Case ASCII Conversion 185
4H ASCII to EBCDIC Conversion 187
41 EBCDIC to ASCII Conversion 190
Arithmetic
6A 16-Bit Addition 230
6B 16-Bit Subtraction 233
6C 16-Bit Multiplication 236
6D 16-Bit Division 240
1 60 6502 ASSEMBLY LANGUAGE SUBROUTINES
String Manipulation
8A String Comparison 345
8B String Concatenation 349
8C Find the Position of a Substring 355
8D Copy a Substring from a String 361
8E Delete a Substring from a String 368
8F Insert a Substring into a String 374
Array Operations
9A 8-Bit Array Summation 382
9B 16-Bit Array Summation 385
9C Find Maximum Byte-Length Element 389
9D Find Minimum Byte-Length Element 393
9E Binary Search 397
9F Bubble Sort 403
INTRODUCTION TO THE PROGRAM SECTION 161
Input/Output
10A Read a Line of Characters from a Terminal 418
1 OB Write a Line of Characters to an Output Device 425
IOC Generate Even Parity 428
10D Check Parity 431
10E CRC-16 Checking and Generation 434
10F I/O Device Table Handler 440
10G Initialize I/O Ports 454
10H Delay Milliseconds 460
Interrupts
11A Unbuffered Interrupt-Driven Input/Output Using a 6850 ACIA 464
11B Unbuffered Interrupt/Driven Input/Output Using a 6522 VIA 472
11C Buffered Interrupt-Dri ven Input/Output Using a 6850 ACI A 480
11D Real-Time Clock and Calendar 490
Binary to BCD Conversion (BN2BCD) 4A
Examples
1. Data: (A) *= 6E16 (110 decimal) 2. Data: (A) = B716 (183 decimal)
Result: (A) = 0116 (hundreds digit) Result: (A) = 0116 (hundreds digit)
(Y) = 1016 (tens and ones digits) (Y) = 83|6 (tens and ones digits)
163
164 CODE CONVERSION
BN2BCD:
; CALCULATE 100 *S DIGIT
DIVIDE BY 100
; Y = QUOTIENT
; A = REMAINDER
LDY #0FFH ;START QUOTIENT AT -1
SEC ;SET CARRY FOR INITIAL SUBTRACTION
D100LP:
INY ;ADD 1 TO QUOTIENT
SBC #100 ;SUBTRACT 100
BCS D100LP ;BRANCH IF A IS STILL LARGER THAN 100
ADC #100 ;ADD THE LAST 100 BACK
TAX ;SAVE REMAINDER
TYA
PHA ;SAVE 100'S DIGIT ON THE STACK
TXA ;GET REMAINDER
;DATA
TEMP: .BLOCK 1 TEMPORARY USED TO COMBINE l'S AND 10'S DIGITS
4A BINARY TO BCD CONVERSION (BN2BCD) 165
SAMPLE EXECUTION:
SC0401:
;CONVERT OA HEXADECIMAL TO 10 BCD
LDA #0AH
JSR BN2BCD
BRK ;A=0, Y=10H
.END
BCD to Binary Conversion (BCD2BN) 4B
Examples
1. Data: (A) = 9916 2. Data: (A) = 2316
Time: 38 cycles
166
4B BCD TO BINARY CONVERSION (BCD2BN) 167
BCD2BN:
;MULTIPLY UPPER NIBBLE BY 10 AND SAVE IT
; TEMP := UPPER NIBBLE * 10 WHICH EQUALS UPPER NIBBLE * (8+2)
TAY ;SAVE ORIGINAL VALUE
AND #0F0H ;GET UPPER NIBBLE
LSR A ;DIVIDE BY 2 WHICH UPPER NIBBLE * 8
STA TEMP ;SAVE * 8
LSR A ;DIVIDE BY 4
LSR A ;DIVIDE BY 8: A = UPPER NIBBLE * 2
CLC
ADC TEMP
STA TEMP ;REG A = UPPER NIBBLE * 10
RTS
; DATA
TEMP: .BLOCK 1
SAMPLE EXECUTION:
SC0402:
.END
Binary to Hexadecimal ASCII Conversion
(BN2HEX) 4C
Examples
1. Data: (A) = FBI6 2. Data: (A) = 5916
168
4C BINARY TO HEXADECIMAL ASCII CONVERSION (BN2HEX) 1 69
BN2HEX:
.•SUBROUTINE NASCII
;PURPOSE: CONVERT A HEXADECIMAL DIGIT TO ASCII
;ENTRY: A = BINARY DATA IN LOWER NIBBLE
;EXIT: A = ASCII CHARACTER
;REGISTERS USED: A,P
NASCII:
CMP #10
BCC NAS1 ;BRANCH IF HIGH NIBBLE < 10
CLC
ADC #7 ;ELSE ADD 7 SO AFTER ADDING '0' THE
; CHARACTER WILL BE IN 'A'.-'F1
NAS1:
ADC #f0' ;MAKE A CHARACTER
RTS
SAMPLE EXECUTION:
170 CODE CONVERSION
SC0403:
;C0NVERT 0 TO 'OO1
LDA #0
JSR BN2HEX
BRK ;A='0l=30H, Y=l0l=30H
.END
Hexadecimal ASCII to Binary Conversion
(HEX2BN) 4D
Examples:
1. Data: (A) = 4416 (ASCII D) 2. Data: (A) = 3116 (ASCII 1)
(Y) = 3716 (ASCII 7) (Y) = 4216 (ASCII B)
171
172 CODE CONVERSION
HEX2BN:
PHA ;SAVE HIGH CHARACTER
TYA ;GET LOW CHARACTER
JSR A2HEX ;CONVERT IT
STA TEMP ;SAVE LOW NIBBLE
PLA ;GET THE HIGH CHARACTER
JSR A2HEX ;CONVERT IT
ASL A
ASL A
ASL A
ASL A ;SHIFT HIGH NIBBLE TO THE UPPER 4 BITS
ORA TEMP ;OR IN THE LOW NIBBLE
RTS
;SUBROUTINE: A2HEX
;PURPOSE: CONVERT ASCII TO A HEX NIBBLE
;ENTRY: A = ASCII CHARACTER
;EXIT: A = BINARY VALUE OF THE ASCII CHARACTER
;REGISTERS USED: A,P
A2HEX:
SEC ;SUBTRACT ASCII OFFSET
SBC #•0'
CMP #10
BCC A2HEX1 ;BRANCH IF A IS A DECIMAL DIGIT
SBC #7 ;ELSE SUBTRACT OFFSET FOR LETTERS
A2HEX1:
RTS
;DATA
TEMP: .BLOCK 1
SAMPLE EXECUTION:
4D HEXADECIMAL ASCII TO BINARY CONVERSION (HEX2BN) 173
SC0404:
;CONVERT 'C71 TO C7 HEXADECIMAL
LDA #'C
LDY #'7'
JSR HEX2BN
BRK ;A=C7H
.END
Conversion of a Binary Number to Decimal ASCII
(BN2DEC) 4E
Examples
1. Data: Value to convert = 3EB716 2. Data: Value to convert = FFC816
174
4E BINARY NUMBER TO ASCII DECIMAL STRING (BN2DEC) 175
;PROGRAM
BN2DEC:
;SAVE PARAMETERS
PLA
STA RETADR ;SAVE LOW BYTE OF RETURN ADDRESS
PLA
STA RETADR+1 ;SAVE HIGH BYTE
PLA
STA VALUE ;SAVE LOW BYTE OF VALUE
PLA
STA VALUE+1 ;SAVE HIGH BYTE OF THE VALUE TO CONVERT
STA NGFLAG ;SAVE MSB OF VALUE AS SIGN OF VALUE
BPL GETBP ;BRANCH IF VALUE IS POSITIVE
LDA #0 ;ELSE TAKE ABSOLUTE VALUE (0 - VALUE)
SEC
SBC VALUE
STA VALUE
176 CODE CONVERSION
LDA #0
SBC VALUE+1
STA VALUE+1
GETBP:
PLA ;SAVE STARTING ADDRESS OF OUTPUT BUFFER
STA BUFPTR
PLA
STA BUFPTR+1
LDX #16
CLC ;CLEAR CARRY
DVLOOP:
ROL VALUE ;SHIFT THE CARRY INTO DIVIDEND BIT 0
ROL VALUE+1 ;WHICH WILL BE THE QUOTIENT
ROL MOD10 ;AND SHIFT DIVIDEND AT THE SAME TIME
ROL MODI0+1
DECCNT:
DEX
BNE DVLOOP
CONCH:
LDA MOD10
CLC
ADC #'0- ;CONVERT 0..9 TO ASCII
JSR CONCAT
EXIT:
LDA NGFLAG
BPL POS ;BRANCH IF ORIGINAL VALUE WAS POSITIVE
LDA ;ELSE
JSR CONCAT ; PUT A MINUS SIGN IN FRONT
POS:
LDA RETADR+1
PHA
LDA RETADR
PHA
RTS ;RETURN
SUBROUTINE: CONCAT
;PURPOSE: CONCATENATE THE CHARACTER IN REGISTER A TO THE
FRONT OF THE STRING ACCESSED THROUGH BUFPTR
;ENTRY: BUFPTR[0] = LENGTH
;EXIT: REGISTER A CONCATENATED (PLACED IMMEDIATELY AFTER THE LENGTH BYTE)
;REGISTERS USED: A,P,Y
CONCAT:
PHA ;SAVE THE CHARACTER ON THE STACK
EXITMR:
PLA ;GET THE CHARACTER BACK FROM THE STACK
LDY #1
STA (BUFPTR),Y ;STORE THE CHARACTER
LDY #0
LDA (BUFPTR),Y ;GET LENGTH BYTE
178 CODE CONVERSION
CLC
ADC #1 ;INCREMENT LENGTH BY 1
STA (BUFPTR),Y ;UPDATE LENGTH
RTS
;DATA
RETADR: .BLOCK 2 ;SAVE RETURN ADDRESS
NGFLAG: .BLOCK 1 ;SIGN OF ORIGINAL VALUE
VALUE: .BLOCK 2 ;VALUE TO CONVERT
MOD10: .BLOCK 2 ;MODULO 10 TEMPORARY
SAMPLE
SC0405:
;CONVERT 0 TO '0■
LDA BUFADR+1 ;HIGH BYTE OF BUFFER ADDRESS
PHA
LDA BUFADR ;LOW BYTE BUFFER ADDRESS
PHA
LDA VALUE1+1 ;HIGH BYTE OF VALUE
PHA
LDA VALUE1 ;LOW BYTE OF VALUE
PHA
JSR BN2DEC ;CONVERT
BRK ;BUFFER SHOULD = "01
• END
Conversion of ASCII Decimal to Binary
(DEC2BN) 4F
digits to two bytes of binary data. Note that Program Size: 171 bytes
the length is an ordinary binary number, not Data Memory Required: Four bytes anywhere in
RAM for an index, a two-byte accumulator
an ASCII number.
(starting address ACCUM), and a flag indicating
Procedure: The program sets a flag if the the sign of the number (address NGLAG), two-
first ASCII character is a minus sign and skips bytes on page zero for a pointer to the string
(address BUFPTR, taken as 00F016 and 00F116 in
over a leading plus sign. It then converts each
the listing).
subsequent digit to decimal (by subtracting
Special Cases:
ASCII zero), multiplies the previous digits by
1. If the string contains something other than a
ten (using the fact that 10=8 + 2, so a leading sign or a decimal digit, the program
multiplication by ten can be reduced to left retuins with the Carry flag set to 1. The result in
registers A and Y is invalid.
shifts and additions), and adds the new digit
2. If the string contains only a leading sign
to the product. Finally, the program subtracts (ASCII + or ASCII -), the program returns
the result from zero if the original number with the Carry flag set to 1 and a result of zero.
was negative. The program exits
immediately, setting the Carry flag, if it finds
something other than a leading sign or a
decimal digit in the string.
Examples
1. Data: String consists of Result: (A) = 0416 (more significant byte of
04 (number of bytes in string) binary data)
31 (ASCII 1) (Y) = C2l6 (less significant byte of
32 (ASCII 2) binary data)
33 (ASCII 3) That is, the number -I- 1234l0 = 04C2!
34 (ASCII 4)
That is, the number is +1,23410.
180
4F ASCII DECIMAL STRING TO BINARY NUMBER (DEC2BN) 181
2. Data: String consists of Result: (A) = 8016 (more significant byte of binary
06 (number of bytes in string) dfta> .
2D (ASCII —) ™ = ^16 (less significant byte of binary
33 (ASCII 3) data>
32 (ASCII 2) That is, the number —32,75010 = 801216.
37 (ASCII 7)
35 (ASCII 5)
30 (ASCII 0)
That is, the number is -32,75010.
;
; Purpose: Convert ASCII characters to two bytes of binary ;
; data. ?
;
; Entry: Register A = high byte of string address ;
; Register Y = low byte of string adddress ;
; The first byte of the string is the length of ;
; the string. ?
;
; Exit: Register A = High byte of the value ;
; Register Y - Low byte of the value ;
IF NO ERRORS TH|EN
CARRY FLAG = 0
. ELSE ;
; CARRY FLAG = 1
;
; Registers used: All ?
;
; Time: Approximately 670 cycles ;
;
; Size: Program 171 bytes ;
; Data 4 bytes plus ;
; 2 bytes in page zero ;
;
;INITIALIZE
LDY #0
LDA (BUFPTR),Y ;GET LENGTH
TAX ; TO REGISTER X
LDA #1
STA INDEX ;INDEX := 1
LDA #0
STA ACCUM ;ACCUM := 0
STA ACCUM+1
STA NGFLAG ;SIGN OF NUMBER IS POSITIVE
CNVERT:
LDY INDEX
LDA (BUFPTR),Y ;GET NEXT CHARACTER
CHKDIG: CMP #•0'
BMI EREXIT ;ERROR IF < '01 (NOT A DIGIT)
CMP #'9'+l
BPL EREXIT ;ERROR IF > '9' (NOT A DIGIT)
PHA ;SAVE THE DIGIT ON THE STACK
LDA NGFLAG
BPL OKEXIT ;BRANCH IF THE VALUE WAS POSITIVE
LDA #0 ,-ELSE REPLACE RESULT WITH -RESULT
SEC
SBC ACCUM
STA ACCUM
LDA #0
SBC ACCUM+1
STA ACCUM+1
EREXIT:
SEC
EXIT:
LDA ACCUM+1 ;GET HIGH BYTE OF VALUE
LDY ACCUM
RTS
;DATA
INDEX: .BLOCK 1 ;INDEX INTO THE STRING
ACCUM: .BLOCK 2 ;ACCUMULATED VALUE (2 BYTES)
NGFLAG: .BLOCK 1 ;SIGN OF NUMBER
SAMPLE EXECUTION:
184 CODE CONVERSION
SC0406:
;CONVERT I1234I TO 04D2 HEX
LDA ADRS1+1
LDY ADRS1 ;AY = ADDRESS OF SI
JSR DEC2BN
BRK ;A = 04, Y = D2 HEX
• END
Lower-Case to Upper-Case Translation (LC2UC) 4G
Procedure: The program determines from Execution Time: 18 cycles if the original
character is valid, fewer cycles otherwise.
comparisons whether the data is an ASCII
lower-case letter. If it is, the program Program Size: 12 bytes
subtracts 2016 from it, thus converting it to its Data Memory Required: None
Examples
1. Data: (A) = 6216 (ASCII b) 2. Data: (A) = 74,6 (ASCII t)
185
186 CODE CONVERSION
LC2UC:
CMP fa'
BCC $1 ;BRANCH IF < 'a1
CMP #' z '
BCS EXIT ;BRANCH IF > 'z'
SEC
SBC #20H ;CHANGE 'a'.-'z' into 'A'.-'Z
EXIT:
RTS
SAMPLE EXECUTION:
SC0407:
;CONVERT LOWER CASE E TO UPPER CASE
LDA #'e'
JSR LC2UC
BRK ;A=IEI=45H
Examples
1. Data: (A) = 3516 (ASCII 5) 3. Data: (A) - 2A16 (ASCII*)
187
188 CODE CONVERSION
Time: 14 cycles
ASC2EB:
AND #7FH ;BE SURE BIT 7=0
TAY ;USE ASCII AS INDEX INTO EBCDIC TABLE
LDA EBCDICfY ;GET EBCDIC
RTS
SAMPLE EXECUTION:
SC0408:
;CONVERT ASCII 'A1
LDA #'A» ;ASCII 'A1
JSR ASC2EB
BRK ;EBCDIC 'A1 ■ 0C1H
Examples
1. Data: (A) = 8516 (EBCDIC e) 2. Data: (A) = 4E16 (EBCDIC + )
Time: 12 cycles
190
41 EBCDIC TO ASCII CONVERSION (EB2ASC) 191
EB2ASC:
TAY
LDA ASCII,Y ;TRANSLATE
RTS
y z ;EBCDIC
.BYTE V ,'z1 f000H,000H,000H,000H,000H,000H ;ASCII
;EBCDIC
.BYTE 000H,000H,000H,000H,000H,000H,000H,000H ;ASCII
;EBCDIC
.BYTE 000H,000H,000H,000H,000H,000H,000H,000H ;ASCII
A B C D E P G ;EBCDIC
.BYTE OOOHj'A1 ,'B1 ,'C' ,'D1 ,'E1 ,'P1 ,'G1 ;ASCII
H I ;EBCDIC
.BYTE 'H1 ,'I' ,0OOH,OOOH,OOOH,O0OH,OOOHrOOOH ;ASCII
J K L M N 0 P ;EBCDIC
.BYTE 000H,'J' ,'K1 ,'L1 ,'M1 ,'N' ,'O« ,'P1 ;ASCII
Q R ;EBCDIC
.BYTE 'Q1 ,'R1 ,000H,000H,000H,000H,000H,000H ;ASCII
S T U V W X ;EBCDIC
.BYTE 000H,000H, 'S' ,'T1 ,'U1 ,'V1 ,'W, 'X1 ;ASCII
Y Z ;EBCDIC
.BYTE 'Y1 ,'Z1 ^OOOH^OOOH^OOH^OOOH^OOOH.OOOH ;ASCII
01234567 ; EBCDIC
.BYTE '01 j'l1 fI2l .'31 .M1 f'5' ,'6' ,'7' ;ASCII
9 ;EBCDIC
.BYTE '9' ,000H,000Hf000H,00UH,000H,000H,000H ;ASCII
SAMPLE EXECUTION:
SC0409:
;CONVERT EBCDIC 'A1
LDA #OC1H ;EBCDIC 'A1
JSR EB2ASC
BRK ;ASCII 'A1 = 041H
Places a specified value in each byte of a one loop, since 8-bit counters can be used
memory area of known size, starting at a instead of a 16-bit counter. The approach
given address. does, however, require somewhat more
Procedure: The program fills all the whole memory than a single loop with a 16-bit
pages with the specified value first and then counter. A size of 000016 causes an exit with
fills the remaining partial page. This approach no memory changed.
is faster than dealing with the entire area in
193
194 ARRAY MANIPULATION
Examples
1. Data: Value - FF, 2. Data: Value = EA16 (6502 operation
16
MFILL:
;POP THE PARAMETERS FROM THE STACK
PLA
5A MEMORY FILL (MFILL) 1 95
STA RETADR
PLA
STA RETADR+1 ;GET THE RETURN ADDRESS
PLA
STA VALUE ;GET FILL VALUE
PLA
STA ARYSZ
PLA
STA ARYSZ+1 ;GET SIZE OF AREA
PLA
STA ARYPTR
PLA
STA ARYPTR+1 ;GET STARTING ADDRESS OF AREA
LDA RETADR+1
PHA
LDA RETADR
PHA ;RESTORE RETURN ADDRESS
EXIT:
RTS
; DATA
ARYSZ: .BLOCK 2 ;NUMBER OF BYTES TO INITIALIZE
VALUE: .BLOCK 1 ;VALUE TO INITIALIZE ARRAY WITH
196 ARRAY MANIPULATION
SAMPLE EXECUTION
SC0501:
;FILL A SMALL BU
LDA BF1ADR+1
PHA
LDA BF1ADR
PHA ;PUSH STARTING ADDRESS
LDA BF1SZ+1
PHA
LDA BF1SZ
PHA ;PUSH NUMBER OF BYTES
LDA #0
PHA ;PUSH VALUE
JSR MFILL ;FILL BUFFER
BRK
.END
Block Move (BLKMOV) 5B
Important Note: The user should be careful 2. Moving data to or from areas occupied or
used by the program itself will produce unpredic
if either the source or the destination area table results. Obviously, moving data to or from
includes the temporary storage used by the page 0 requires caution, since both this routine
and most systems programs use that page. This
program itself. The program provides auto
routine does provide automatic address wrap
matic address wraparound (mod 64K), but around (mod 64K) for consistency, but the user
the results of any move involving the pro must still approach moves involving page 0
carefully.
gram's own temporary storage are unpredic
table.
197
1 98 ARRAY MANIPULATION
Less significant byte of return address The block of memory is moved from the
More significant byte of return address source area to the destination area. If the
number of bytes to be moved is NBYTES,
Less significant byte of number of bytes
the lowest address in the destination area is
to move
DEST, and the lowest address in the source
More significant byte of number of
area is SOURCE, then the area from
bytes to move
addresses SOURCE through SOURCE +
Less significant byte of lowest address NBYTES - 1 is moved to addresses DEST
of destination area through DEST + NBYTES - 1.
More significant byte of lowest address
of destination area
Examples
1. Data: Number of bytes to move = 020016 Note that Example 2 presents a more com
Lowest address in destination area
plex problem than Example 1 because the
= O5D116
Lowest address in source area source and destination areas overlap. If, for
- 035E16 instance, the program were simply to move
Result: The contents of memory locations data to the destination area starting from the
035E16 through 055D16 are moved
lowest address, it would initially move the
to O5D116 through 07D016.
2. Data: Number of bytes to move
contents of C30016 to C94616. This would
- 1B7A16 destroy the old contents of C94616, which are
Lowest address in destination needed later in the move. The solution to this
area = C94616
Lowest address in source area
problem is to move the data starting from the
- C30016 highest address if the destination area is
Result: The contents of memory locations above the source area but overlaps it.
C30016 through DE7916 are moved
to C94616 through E4BF16
5B BLOCK MOVE (BLKMOV) 1 99
BLKMOV:
;GET RETURN ADDRESS
PLA
TAY ;SAVE LOW BYTE
PLA
TAX ;SAVE HIGH BYTE
EXIT:
RTS
5B BLOCK MOVE (BLKMOV) 201
*******************************************
SUBROUTINE: MVELFT
;PURPOSE: MOVE SOURCE TO DESTINATION STARTING FROM
; THE LOWEST ADDRESS
;ENTRY: MVSRCE = 2 BYTE LOWEST ADDRESS OF SOURCE AREA
; MVDEST = 2 BYTE LOWEST ADDRESS OF DESTINATION AREA
MVELEN = 2 BYTE NUMBER OF BYTES TO MOVE
;EXIT: SOURCE MOVED TO DESTINATION
********************************************
MVELFT:
LDY #0 ;ZERO INDEX
LDX MVELEN+1 ;X= NUMBER OF FULL PAGES TO MOVE
BEQ MLPART ;IF X = 0 THEN DO PARTIAL PAGE
MLPAGE:
LDA (MVSRCE),Y
STA (MVDEST),Y ;MOVE ONE BYTE
INY ;NEXT BYTE
BNE MLPAGE ;CONTINUE UNTIL 256 BYTES ARE MOVED
INC MVSRCE+1 ;ADVANCE TO NEXT PAGE OF SOURCE
INC MVDEST+1 ; AND DESTINATION
DEX ;DECREMENT PAGE COUNT
BNE MLPAGE ;CONTINUE UNTIL ALL FULL PAGES ARE MOVED
MLPART:
LDX MVELEN ;GET LENGTH OF LAST PAGE
BEQ MLEXIT ;BRANCH IF LENGTH OF LAST PAGE
;REGISTER Y IS 0
MLLAST:
LDA (MVSRCE),Y
STA (MVDEST),Y ;MOVE BYTE
INY ;NEXT BYTE
DEX /•DECREMENT COUNTER
BNE MLLAST /•CONTINUE UNTIL LAST PAGE IS DONE
MLEXIT:
RTS
/•SUBROUTINE: MVERHT
/•PURPOSE: MOVE SOURCE TO DESTINATION STARTING FROM
; THE HIGHEST ADDRESS
;ENTRY: MVSRCE = 2 BYTE LOWEST ADDRESS OF SOURCE AREA
; MVDEST = 2 BYTE LOWEST ADDRESS OF DESTINATION AREA
; MVELEN = 2 BYTE NUMBER OF BYTES TO MOVE
;EXIT: SOURCE MOVED TO DESTINATION
;********************************************
MVERHT:
LDA MVELEN+1
CLC
ADC MVDEST+1
STA MVDEST+1 ;POINT TO LAST PAGE OF DESTINATION
MRO:
DEY ;BACK UP Y TO THE NEXT BYTE
LDA (MVSRCE),Y
STA (MVDEST),Y ;MOVE BYTE
CPY #0
BNE MRO 7BRANCH IF NOT DONE WITH THE LAST PAGE
MRPAGE:
LDX MVELEN+1 ;GET HIGH BYTE.OF COUNT AS PAGE COUNTER
BEQ MREXIT ;BRANCH IF HIGH BYTE = 0 (NO FULL PAGES)
MR1:
DEC MVSRCE+1 ;BACK UP TO PREVIOUS PAGE OF SOURCE
DEC MVDEST+1 ; AND DESTINATION
MR2:
DEY ;BACK UP Y TO THE NEXT BYTE
LDA (MVSRCE),Y
STA (MVDEST) ,Y ;MOVE BYTE
CPY #0
BNE MR2 ;BRANCH IF NOT DONE WITH THIS PAGE
DEX ;DECREMENT PAGE COUNTER
BNE MR1 ;BRANCH IF NOT ALL PAGES ARE MOVED
MREXIT:
RTS
;DATA SECTION
MVELEN .BLOCK 2 ;LENGTH OF MOVE
SC0502:
LDA SRCE+1
PHA ;PUSH HIGH BYTE OF SOURCE
LDA SRCE
PHA ;PUSH LOW BYTE OF SOURCE
LDA DEST+1
PHA ;PUSH HIGH BYTE OF DESTINATION
LDA DEST
PHA ;PUSH LOW BYTE OF DESTINATION
5B BLOCK MOVE (BLKMOV) 203
LDA LEN+1
PHA ;PUSH HIGH BYTE OF LENGTH
LDA LEN
PHA ;PUSH LOW BYTE OF LENGTH
JSR BLKMOV ;MOVE DATA FROM SOURCE TO DESTINATION
BRK ; FOR THE DEFAULT VALUES MEMORY FROM 800 HEX
; THROUGH 97F HEX IS MOVED TO 900 HEX THROUGH
? A7F HEX.
JMP SC0502
.END ;PROGRAM
One-Dimensional Byte Array Index (D1BYTE) 5C
Examples
Base address = 0E0016 2. Data: Baseaddress = C4El16
1. Data:
Subscript = 012C16 Subscript = 02E416
204
5C ONE-DIMENSIONAL BYTE ARRAY INDEX (D1 BYTE) 205
Time: 74 cycles •
DlBYTE:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
•GET SUBSCRIPT
PLA
STA SS
PLA
STA SS+1
LDA RETADR
PHA ;RESTORE RETURN ADDRESS
; DATA
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SS: .BLOCK 2 ;SUBSCRIPT INTO THE ARRAY
SAMPLE EXECUTION:
SC0503:
;PUSH ARRAY ADDRESS
LDA ARYADR+1 ;HIGH BYTE
PHA
LDA ARYADR ;LOW BYTE
PHA
;PUSH A SUBSCRIPT
LDA SUBSCR+1 ;HIGH BYTE
PHA
LDA SUBSCR ;LOW BYTE
PHA
JSR D1BYTE ;CALCULATE ADDRESS
BRK ;AY « ARY+2
= ADDRESS OF ARY(2), WHICH CONTAINS 3
JMP SC0503
• END ;PROGRAM
One-Dimensional Word Array Index (D1WORD) 5D
Examples
1. Data: Base address = A14816 2. Data: Base address = C4E016
Subscript = 01A916 Subscript - 015B16
Result: Address of first byte of element Result: Address of first byte of element
- A14816 + 2 x 01A916
16 16 = C4EO16 + 2XO15B16
A1481616 + 0342 16
16 A4 916
A49A 16. 0 16 + 02B6 16 C 16
That is, the word-length element That is, the word-length element
occupies addresses A49A16 and A49B16. occupies addresses C79616 and C79716.
207
208 ARRAY MANIPULATION
Time: 78 cycles
DlWORD:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
LDA RETADR+1
PHA
LDA RETADR
PHA ;RESTORE RETURN ADDRESS
; DATA
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SS: .BLOCK 2 ;SUBSCRIPT INTO THE ARRAY
SAMPLE EXECUTION: ;
SC0504:
;PUSH ARRAY ADDRESS
LDA ARYADR+1
PHA
LDA ARYADR
PHA
;PUSH A SUBSCRIPT OF 3
LDA SUBSCR+1
PHA
LDA SUBSCR
PHA
JSR D1W0RD ;CALCULATE ADDRESS
BRK ;FOR THE INITIAL TEST DATA
;AY = STARTING ADDRESS OF ARY(3)
= ARY + (3*2)
= ARY + 6
; = ARY(3) CONTAINS 240 HEX
JMP SC0504
;TEST DATA
SUBSCR: .WORD 3 ;TEST SUBSCRIPT INTO ARY
ARYADR: .WORD ARY ;BASE ADDRESS OF ARRAY
210
5E TWO-DIMENSIONAL BYTE ARRAY INDEX (D2BYTE) 211
Examples
1. Data: Base address = 3C00'16 The general formula is
Column subscript = 0004i6 ADDRESS OF ELEMENT - BASE ADDRESS
Size of row (number of columns) OF ARRAY + ROW SUBSCRIPT x SIZE OF ROW
= 001816 + COLUMN SUBSCRIPT
Row subscript = 000316
Result: Address of element = 3C0016 Note that we refer to the size of the row
+ 000316 x 001816 + 000416 subscript; the size is the number of consecu
= 3C0016 + 004816 + 000416
tive memory addresses for which the
= 3C4C16.
Thus the address of ARRAY (3,4) subscript has the same value. This is also the
is 3C4C16. number of bytes from the starting address of
an element to the starting address of the ele
2. Data: Base address = 6A4A16 ment with the same column subscript but a
Column subscript = 0035,6
Size of row (number of columns) row subscript one larger.
= 005016
Row subscript = 000216
D2BYTE:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;MULTIPLY FIRST SUBSCRIPT * ROW LENGTH USING THE SHIFT AND ADD
; ALGORITHM. THE RESULT WILL BE IN SSI
LDA #0 ;PARTIAL PRODUCT = ZERO INITIALLY
STA PROD
STA PROD+1
LDX #17 ;NUMBER OF SHIFTS =17
CLC
5E TWO-DIMENSIONAL BYTE ARRAY INDEX (D2BYTE) 213
MULLP:
ROR PROD+1 ;SHIFT PARTIAL PRODUCT
ROR PROD
ROR SS1+1 ;SHIFT MULTIPLIER
ROR SSI
BCC DECCNT
CLC ;ADD MULTIPLICAND TO PARTIAL PRODUCT
LDA SS1SZ ; IF NEXT BIT OF MULTIPLIER IS 1
ADC PROD
STA PROD
LDA SS1SZ+1
ADC PROD+1
STA PROD+1
DECCNT:
DEX
BNE MULLP
; DATA
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SSI: .BLOCK 2 ;SUBSCRIPT 1
SS1SZ: .BLOCK 2 ;SIZE OF SUBSCRIPT 1 IN BYTES
SS2: .BLOCK 2 ;SUBSCRIPT 2
PROD: .BLOCK 2 ;TEMPORARY FOR THE MULTIPLY
214 ARRAY MANIPULATION
SAMPLE EXECUTION:
SC0505:
;PUSH ARRAY ADDRESS
LDA ARYADR+1
PHA
LDA ARYADR
PHA
; DATA
SUBS1: .WORD 2 ;SUBSCRIPT 1
SSUBS1: .WORD 8 ;SIZE OF SUBSCRIPT 1
SUBS2: .WORD 4 ;SUBSCRIPT 2
ARYADR: -.WORD ARY ;ADDRESS OF ARRAY
• END ;PROGRAM
Two-Dimensional Word Array Index (D2WORD) 5F
215
216 ARRAY MANIPULATION
Examples
1. Data: Base address = 5E14i6 The general formula is
Column subscript = 0008l6 STARTING ADDRESS OF ELEMENT
Size of a row (in bytes) - 001Ci6 - BASE ADDRESS OF ARRAY
(i.e., each row has 001410 or 000El6 + ROW SUBSCRIPT x SIZE OF ROW
word-length elements) + COLUMN SUBSCRIPT x 2
Row subscript = 000516
D2WORD:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;MULTIPLY FIRST SUBSCRIPT * ROW SIZE (IN BYTES) USING THE SHIFT AND ADD
ALGORITHM. THE RESULT WILL BE IN SSI
LDA #0 ;PARTIAL PRODUCT « ZERO INITIALLY
STA PROD
STA PROD+1
LDX #17 ;NUMBER OF SHIFTS =17
CLC
MT1T T D •
MULLF:
DECCNT:
DEX
BNE MULLP
;
;DATA
5F TWO-DIMENSIONAL WORD ARRAY INDEX (D2WORD) 219
SAMPLE EXECUTION:
SC0506:
;PUSH ARRAY ADDRESS
LDA ARYADR+1
PHA
LDA ARYADR
PHA
; DATA
SUBS1: .WORD 2 ;SUBSCRIPT 1
SSUBS1: .WORD 16 ;SIZE OF SUBSCRIPT 1
SUBS2: .WORD 4 ;SUBSCRIPT 2
ARYADR: .WORD ARY ;ADDRESS OF ARRAY
.END ;PROGRAM
N-Dimensional Array Index (NDIM) 5G
(in bytes); the size of the next subscript is the Special Case: If the number of dimensions is
size of the elements times the maximum zero, the program returns with the base address
in registers A (more significant byte) and Y (less
value of the rightmost subscript plus 1, etc. significant byte).
All subscripts are assumed to begin at zero;
otherwise, the user must normalize the
subscripts (see the second example at the end
of the listing). left shifts. Otherwise, it performs each
Procedure: The program loops on each multiplication using the shift-and-add
dimension, calculating the offset in that algorithm of Subroutine 6H. Once the pro
dimension as the subscript times the size. If gram has calculated the overall offset, it adds
the size is an easy case (an integral power of that offset to the base address to obtain the
2), the program reduces the multiplication to starting address of the element.
221
222 ARRAY MANIPULATION
Less significant byte of size of rightmost The element occupies memory addresses
dimension from the calculated starting address through
More significant byte of size of right that address plus the rightmost subscript
most dimension minus 1. That is, the element occupies
memory addresses START through START
Less significant byte of rightmost
+ SIZE - 1, where START is the calculated
subscript
address and SIZE is the size of an element in
More significant byte of rightmost
bytes.
subscript
Example
Data: Base address = 3C0016 where:
Number of dimensions == 0316 N is the number of dimensions
Rightmost subscript = 000516 SUBSCRIPTj is the /th subscript
Rightmost size = 000316 (3-byte entries) SIZE} is the size of the /th dimension
Middle subscript = 000316
Middle size = 001216 (six 3-byte entries)
Leftmost subscript = 0004i6 Note that we use the sizes of each dimen
Leftmost size = 007E16 (seven sets of six sion as parameters to reduce the number of
3-byte entries)
repetitive multiplications and to generalize
Result: Address of entry = 3C0016 + 000516 x
16
the procedure. The sizes can be calculated
000316
0003 16 + 000316
0003 16 x 001216
0012 16 + 000416
0004 6 (and saved) as soon as the bounds of the
007E
x 007E16 3C00
= 3C0016 + 000F 16 + 0036
0 16
array are known. Those sizes can then be
+ 01F816 = 3E3D16.
used whenever indexing is performed on that
That is, the element is ARRAY (4,3,5); it
occupies addresses 3E3D16 through array. Obviously, the sizes do not change if
3E3F16. The maximum values of the the bounds are fixed and they should not be
various subscripts are 6 (leftmost) and 5
recalculated as part of each indexing opera
(middle). Each element consists of three
bytes. tion. The sizes are also general, since the ele
The general formula is ments can themselves consist of any number
STARTING ADDRESS = BASE ADDRESS of bytes.
£ SUBSCRIPT; x SIZEj
NDIM:
;POP PARAMETERS
PLA
STA RETADR
PLA
STA RETADR+1 ;SAVE RETURN ADDRESS
PLA
STA NUMDIM ;GET NUMBER OF DIMENSIONS
;OFFSET := 0
LDA #0
STA OFFSET
STA OFFSET+1
STA SS+1
ADBASE:
;CALCULATE THE STARTING ADDRESS OF THE ELEMENT
;OFFSET = BASE + OFFSET
PLA ;GET LOW BYTE OF BASE
CLC
ADC OFFSET ;ADD LOW BYTE OF OFFSET
STA OFFSET
PLA ;GET HIGH BYTE OF BASE
ADC OFFSET+1 ;A = HIGH BYTE OF BASE + OFFSET
STA OFFSET+1
;SUBROUTINE NXTOFF
;PURPOSE: OFFSET := OFFSET + (SUBSCRIPT * SIZE);
;ENTRY: OFFSET = CURRENT OFFSET
; SUBSCRIPT = CURRENT SUBSCRIPT
SIZE = CURRENT SIZE OF THIS DIMENSION
;EXIT: OFFSET = OFFSET + (SUBSCRIPT * SIZE);
jREGISTERS USED: ALL
NXTOFF:
LDA SIZE
LDY #0 ;Y=INDEX INTO EASY ARRAY
LDX #SZEASY ;X=SIZE OF EASY ARRAY
EASYLP:
CMP EASYAY,Y
BEQ ISEASY ;BRANCH IF SIZE IS AN EASY ELEMENT
INY ;INCREMENT INDEX
DEX ;DECREMENT COUNT
BNE EASYLP ;BRANCH IF NOT THROUGH ALL EASY ELEMENTS
BEQ BIGSZ ;BRANCH IF SIZE IS NOT EASY
226 ARRAY MANIPULATION
ISEASY:
CPY #0
BEQ ADDOFF ;BRANCH IF SHIFT FACTOR = 0
BIGSZ:
;SIZE IS NOT AN EASY MULTIPLICATION SO PERFORM MULTIPLICATION OF
; ELEMENT SIZE AND SUBSCRIPT THE HARD WAY
LDA #0 ;PARTIAL PRODUCT = ZERO INITIALLY
STA PROD
STA PROD+1
LDX #17 ;NUMBER OF SHIFTS =17
CLC
MULLP:
ROR PROD+1 ;SHIFT PARTIAL PRODUCT
ROR PROD
ROR SS+1 ;SHIFT MULTIPLIER
ROR SS
BCC DECCNT
CLC ;ADD MULTIPLICAND TO PARTIAL PRODUCT
LDA SIZE ; IF NEXT BIT OF MULTIPLIER IS 1
ADC PROD
STA PROD
LDA SIZE+1
ADC PROD+1
STA PROD+1
DECCNT:
DEX
BNE MULLP
ADDOFF:
LDA SS
CLC
ADC OFFSET ;ADD LOW BYTES
STA OFFSET
LDA SS+1
ADC OFFSET+1 ;ADD HIGH BYTES
STA OFFSET+1
RTS
.BYTE 8 ;3
.BYTE 16. ;4
.BYTE 32. ;5
.BYTE 64. ;6
.BYTE 128. ;7
SZEASY .EQU $-EASYAY
;DATA
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SS: .BLOCK 2 ;SUBSCRIPT INTO THE ARRAY
SIZE: .BLOCK ;SIZE OF AN ARRAY ELEMENT
OFFSET: .BLOCK ;TEMPORARY FOR CALCULATING
NUMDIM: .BLOCK ;NUMBER OF DIMENSIONS
PROD: .BLOCK ;TEMPORARY FOR MULTIPLICATION IN NXTOFF
SAMPLE EXECUTION:
;PROGRAM SECTION
SC0507:
;
;FIND ADDRESS OF AYl[1,3,0]
; SINCE LOWER BOUNDS OF ARRAY 1 ARE ALL ZERO IT IS NOT
; NECESSARY TO NORMALIZE THEM
PHA
LDA #A2SZ2
PHA
JMP SC0507
;DATA
AY1ADR: .WORD AY1 ;ADDRESS OF ARRAY 1
AY2ADR: • WORD AY 2 ;ADDRESS OF ARRAY 2
.END ;PROGRAM
16-Bit Addition (ADD16) 6A
stack. All 16-bit numbers are stored in the Execution Time: 80 cycles
usual 6502 style with the less significant byte Program Size: 38 bytes
on top of the more significant byte. Data Memory Required: Four bytes anywhere in
Procedure: The program clears the Carry memory for the second operand (two bytes start
ing at address ADEND2) and the return address
flag initially and adds the operands one byte
(two bytes starting at address RETADR).
at a time, starting with the less significant
bytes. It sets the Carry flag from the addition
of the more significant bytes.
Examples
1. Data: First operand = 03E116 2. Data: First operand = A45D16
Second operand = 07E416 Second operand = 97E116
230
6A 16-BIT ADDITION (ADD16) 231
Time: 80 cycles
ADD16:
;SAVE THE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;GET ADDEND 2
PLA
STA ADEND2
PLA
STA ADEND2+1
; DATA
ADEND2: .BLOCK 2 ;TEMPORARY FOR ADDEND 2
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SC0601:
;SUM OPRND1 + OPRND2
LDA OPRND1+1
PHA
LDA OPRND1
PHA
LDA OPRND2+1
PHA
LDA OPRND2
PHA
JSR ADD 16
PLA
TAY
PLA
BRK ;A ■ HIGH BYTE, Y = LOW BYTE
JMP SC0601
•END ;PROGRAM
16-Bit Subtraction (SUB16) 6B
Less significant byte of return address Less significant byte of difference (minuend
More significant byte of return address — subtrahend)
More significant byte of difference (minuend
Less significant byte of subtrahend
— subtrahend)
More significant byte of subtrahend
Examples
1. Data: Minuend = A45D16 2. Data: Minuend » 03El16
Subtrahend = 97E116 Subtrahend = 07E416
233
234 ARITHMETIC
; Time: 80 cycles
SUB16:
;SAVE THE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;GET SUBTRAHEND
PLA
STA SUBTRA
PLA
STA SUBTRA+1
; DATA
SUBTRA: .BLOCK 2 ;TEMPORARY FOR SUBTRAHEND
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
;
;
SAMPLE EXECUTION ;
;
;
SC0602:
;SUBTRACT OPRND2 FROM OPRNDl
LDA OPRND1+1
PHA
LDA OPRNDl
PHA
LDA OPRND2+1
PHA
LDA OPRND2
PHA
JSR SUB 16
PLA
TAY
PLA
BRK ;A = HIGH BYTE, Y = LOW BYTE
JMP SC0602
.END ;PROGRAM
16-Bit Multiplication (MUL16) 6C
Less significant byte of return address Less significant byte of less significant word
More significant byte of return address of product
More significant byte of less significant word
Less significant byte of multiplier
of product
More significant byte of multiplier
Examples
1. Data: Multiplier = 001216 (1810) 2. Data: Multiplier = 37D116.(14,2891O)
Multiplicand = 03Dl16 (977,0) Multiplicand = A04516 (41,02910)
236
6C 16-BIT MULTIPLICATION (MUL16) 237
Note that MUL16 returns only the less user should note that it is correct only if the
significant word of the product to maintain operands are unsigned. If the operands are
compatibility with other 16-bit arithmetic signed numbers and either one is negative,
operations. The more significant word of the the user must determine the sign of the pro
product is available in memory locations duct and replace negative operands with their
HIPROD (less significant byte) and absolute values (two's complements) before
HIPROD + 1 (more significant byte), but the calling MUL16.
;
MUL16:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;GET MULTIPLIER
PLA
STA MLIER
PLA
238 ARITHMETIC
STA MLIER+1
;GET MULTIPLICAND
PLA
STA MCAND
PLA
STA MCAND+1
;PERFORM MULTIPLICATION USING THE SHIFT AND ADD ALGORITHM
; THIS ALGORITHM PRODUCES A UNSIGNED 32 BIT PRODUCT IN
; HIPROD AND MLIER WITH HIPROD BEING THE HIGH WORD.
LDA #0
STA HIPROD ;ZERO HIGH WORD OF PRODUCT
STA HIPROD+1
LDX #17 ;NUMBER OF BITS IN MULTIPLIER PLUS 1, THE
; EXTRA LOOP IS TO MOVE THE LAST CARRY INTO
; THE PRODUCT
CLC ; CLEAR CARRY FOR FIRST TIME THROUGH LOOP
MULLP:
DECCNT:
DEX
BNE MULLP ;CONTINUE UNTIL DONE
;DATA
MCAND: .BLOCK 2 .-MULTIPLICAND
6C 16-BIT MULTIPLICATION (MUL16) 239
SAMPLE EXECUTION:
SC0603:
;MULTIPLY OPRNDl * OPRND2 AND STORE THE PRODUCT AT R
LDA OPRNDl+1
PHA
LDA OPRNDl
PHA
LDA OPRND2+1
PHA
LDA OPRND2
PHA
JSR MUL16 ;MULTIPLY
PLA
STA kESULT
PLA
STA RESULT+1
BRK ;RESULT OF 1023 * -2 = -2046 0F802H
; IN MEMORY RESULT = 02H
RESULT+1 = F8H
JMP SC0603
OPRND1 .WORD -2
OPRND2 • WORD 1023
RESULT: .BLOCK 2 ;2 BYTE RESULT
.END ;PROGRAM
16-Bit Division
(SDIV16, UDIV16, SREM16, UREM16) 6D
240
6D 16-BIT DIVISION (SDIV16. UDIV16, SREM16. UREM16) 241
Examples
1. Data: Dividend = 03E016 = 99210 2. Data: Dividend = D73A16 = —10,43810
Divisor = 00B616 - 182 10 Divisor = O2F116 = 75310
Result: Quotient (from UDIV16) = 000516 Result: Quotient (from SDIV16) = FFF316
Remainder (from UREM16) = 005216 1310
= 008210 Remainder (from SREM16) = FD7716
Carry = 0 (no divide-by-zero error) = ~64910
Carry = 0 (no divide-by-zero error)
Note that we have taken the view that the Regardless of the entry point used, the
remainder of a signed division may be either program always calculates both the quotient
positive or negative. In our procedure, the and the remainder. Upon return, the quo
remainder always takes the sign of the divi tient is available in addresses DVEND and
dend. The user can easily examine the quo DVEND + 1 (more significant byte in
tient and change the form to obtain a DVEND +1) and the remainder in addresses
remainder that is always positive. In that DVEND+ 2 and DVEND+ 3 (more signifi
case, the final result of Example 2 would be cant byte in DVEND+ 3). Thus, the user can
Quotient = FFF216 = -14 10 always obtain the result that is not returned
in the stack.
Remainder (always positive) 0068,
= 104lft
Purpose: SDIV16
Divide 2 signed 16 bit words and return a
16 bit signed quotient.
UDIV16
Divide 2 unsigned 16 bit words and return a
16 bit unsigned quotient.
SREM16
Divide 2 signed 16 bit words and return a
16 bit signed remainder.
UREM16
Divide 2 unsigned 16 bit words and return a
16 bit unsigned remainder.
;UNSIGNED REMAINDER
UREM16:
LDA #2 ;RESULT IS REMAINDER (INDEX=2)
UDIVMD:
STA RSLTIX ;RESULT INDEX (0 FOR QUOTIENT,
2 FOR REMAINDER)
;GET DIVISOR
PLA
STA DVSOR
PLA
STA DVSOR+1
;GET DIVIDEND
PLA
STA DVEND
PLA
6D 16-BIT DIVISION (SDIV16, UDIV16, SREM16, UREM16) 243
STA DVEND+1
;PERFORM DIVISION
JSR UDIV
BCC DIVOK ;BRANCH IF NO ERRORS
DIVER: JMP EREXIT
DIVOK: JMP OKEXIT
;
;SIGNED DIVISION
SDIV16:
LDA #0 ;RESULT IS QUOTIENT (INDEX=0)
BEQ SDIVMD
;SIGNED REMAINDER
SREM16:
LDA #2 ;RESULT IS REMAINDER (INDEX=2)
BNE SDIVMD
SDIVMD:
STA RSLTIX ;RESULT INDEX (0 FOR QUOTIENT,
; 2 FOR REMAINDER)
;GET DIVISOR
PLA
STA DVSOR
PLA
STA DVSOR+1
;GET DIVIDEND
PLA
STA DVEND
PLA
STA DVEND+1
DOREM:
;NEGATE REMAINDER IF IT IS NEGATIVE
LDA SREM
BPL OKEXIT ;BRANCH IF REMAINDER IS POSITIVE
LDA #0
SEC
SBC DVEND+2
STA DVEND+2
LDA #0
SBC DVEND+3
STA DVEND+3
JMP OKEXIT
BCS DVEXIT
DVEXIT:
,-PUSH RESULT
LDX RSLTIX ;GET INDEX TO RESULT (0=QUOTIENT, 2=REMAINDER)
LDA DVEND+1,X
PHA
LDA DVEND,X
PHA
**************************************
;ROUTINE: UDIV
;PURPOSE: DIVIDE A 16 BIT DIVIDEND BY A 16 BIT DIVISOR
;ENTRY: DVEND = DIVIDEND
DVSOR « DIVISOR
;EXIT: DVEND « QUOTIENT
; DVEND+2 = REMAINDER
;REGISTERS USED: ALL
* ***********************************
UDIV:
;ZERO UPPER WORD OF DIVIDEND THIS WILL BE CALLED DIVIDEND [1] BELOW
LDA #0
STA DVEND+2
STA DVEND+3
;
;CHECK IF DIVIDEND[1] IS LESS THAN DIVISOR
246 ARITHMETIC
CHKLT:
SEC
LDA DVEND+2
SBC DVSOR
TAY ;SAVE LOW BYTE IN REG Y
LDA DVEND+3
SBC DVSOR+1 ;SUBTRACT HIGH BYTES WITH RESULT IN REG A
BCC DECCNT ;BRANCH IF DIVIDEND < DIVISOR AND CARRY
STY DVEND+2 ;ELSE
STA DVEND+3 ; DIVIDEND[1] := DIVIDEND[1] - DIVISOR
DECCNT:
DEX
BNE DIVLP
; DATA
DVSOR: .BLOCK 2 ;DIVISOR
DVEND: •BLOCK 2 ;DIVIDEND[O] AND QUOTIENT
.BLOCK 2 ;DIVIDEND[1] AND REMAINDER
RETADR: • BLOCK 2 ;RETURN ADDRESS
SQUOT: .BLOCK 1 ;SIGN OF QUOTIENT
SREM: .BLOCK 1 ;SIGN OF REMAINDER
RSLTIX: .BLOCK 1 ;INDEX TO THE RESULT 0 IS QUOTIENT,
; 2 IS REMAINDER
SAMPLE EXECUTION:
;PROGRAM SECTION
SC0604:
;SIGNED DIVIDE, OPRNDl / OPRND2, STORE THE QUOTIENT AT QUOT
LDA OPRNDl+1
PHA
LDA OPRNDl
PHA
LDA OPRND2+1
PHA
LDA OPRND2
PHA
JSR SDIV16 ;SIGNED DIVIDE
PLA
STA QUOT
PLA
STA QUOT+1
BRK ;RESULT OF -1023 / 123 = -8
; IN MEMORY QUOT = F8 HEX
QUOT+1 * FF HEX
6D 16-BIT DIVISION (SDIV16, UDIV16. SREM16. UREM16) 247
JMP SC0604
; DATA
OPRNDl .WORD -1023 ;DIVIDEND (64513 UNSIGNED)
OPRND2 .WORD 123 ;DIVISOR
QUOT: .BLOCK 2 ;QUOTIENT
REM: .BLOCK 2 ?REMAINDER
.END ;PROGRAM
16-Bit Comparison (CMP16) 6E
Entry Conditions
Order in stack (starting from the top) Exit Conditions
Less significant byte of return address Flags set as if subtrahend had been
More significant byte of return address subtracted from minuend, with a correction if
two's complement overflow occurred.
Less significant byte of subtrahend (top
operand or WORD2) Zero flag = 1 if subtrahend and minuend are
More significant byte of subtrahend equal, 0 if they are not equal.
(top operand or W0RD2) Carry flag = 0 if subtrahend is larger than
Less significant byte of minuend (bottom minuend in the unsigned sense, 1 if it is less
operand or W0RD1) than or equal to the minuend.
More significant byte of minuend (bottom Negative flag = 1 if subtrahend is larger than
operand or WORD1) minuend in the signed sense, 0 if it is less
than or equal to the minuend. This flag is cor
rected if two's complement overflow occurs.
249
250 ARITHMETIC
Examples
1. Data: Minuend (bottom operand) = O3E1,6 3. Data: Minuend (bottom operand) = A45D,6
Subtrahend (top operand) = 07E416 Subtrahend (top operand) = 77E116
2. Data: Minuend (bottom operand) = C51 A,6 In Example 3, the bottom operand is a negative
Subtrahend (topoperand) = C51A,6 two's complement number, whereas the top operand is
a positive two's complement number.
Result: Carry = 1, indicating subtrahend is not
larger in unsigned sense
CMP16:
;SAVE THE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;GET SUBTRAHEND
PLA
STA SUBTRA
PLA
STA SUBTRA+1
;GET MINUEND
PLA
STA MINEND
PLA
STA MINEND+1
LDA MINEND
CMP SUBTRA ;COMPARE LOW BYTES
BEQ EQUAL ;BRANCH IF THEY ARE EQUAL
;DATA
MINEND: .BLOCK 2 ;TEMPORARY FOR THE MINUEND
SUBTRA: .BLOCK 2 ;TEMPORARY FOR THE SUBTRAHEND
RETADR: .BLOCK 2 ;TEMPORARY FOR THE RETURN ADDRESS
;
SAMPLE EXECUTION
SC0605:
;COMPARE OPRND1 AND OPRND2
LDA 1
0PRND1+1
PHA
LDA 0PRND1
PHA
LDA OPRND2+1
PHA
LDA OPRND2
PHA
JSR CMP16
BRK ;LOOK AT THE FLAGS
; FOR 123 AND 1023
C = 0,Z=0,N
JMP SC0605
.END ;PROGRAM
Multiple-Precision Binary Addition (MPBADD) 6F
bers (the one with the starting address lower Program Size: 48 bytes
in the stack). The length of the numbers (in Data Memory Required: Two bytes anywhere in
bytes) is 255 or less. RAM plus four bytes on page 0. The two bytes
anywhere in RAM are temporary storage for the
Procedure: The program clears the Carry return address (starting at address RETADR).
flag initially and adds the operands one byte The four bytes on page 0 hold pointers to the two
at a time, starting with the least significant numbers (starting at addresses AY1PTR and
AY2PTR, respectively). In the listing, AY1PTR
bytes. The final Carry flag reflects the addi is taken as address 00D016 and AY2PTR as
tion of the most significant bytes. The sum address 00D216.
replaces the operand with the starting address Special Case: A length of zero causes an
lower in the stack (array 1 in the listing). A immediate exit with the sum equal to the bottom
operand (i.e., array 1 is unchanged). The Carry
length of 00 causes an immediate exit with no
flag is set to 1.
addition operations.
253
254 ARITHMETIC
Example
Data: Length of operands (in bytes) = 6
Top operand (array 2) = 19D028A193EA16
Bottom operand (array 1) = 293EABF059C716
Result: Bottom operand (array 1) = Bottom
operand (array 1) + Top operand
(array2)=430ED491EDBl16
Carry = 0
EQUATES
AY1PTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 1 POINTER
AY2PTR: .EQU 0D2H ;PAGE ZERO FOR ARRAY 2 POINTER
MPBADD:
;SAVE RETURN ADDRESS
6F MULTIPLE-PRECISION BINARY ADDITION (MPBADD) 255
PLA
STA RETADR
PLA
STA RETADR+1
INITIALIZE
LDY #0
CPX #0 ;IS LENGTH OF ARRAYS = 0 ?
BEQ EXIT ;YES, EXIT
£LC ;CLEAR CARRY
LOOP:
LDA (AY1PTR) ,Y ;GET NEXT BYTE
ADC (AY2PTR) ,Y ;ADD BYTES
STA (AY1PTR) ,Y ;STORE SUM
INY INCREMENT ARRAY INDEX
DEX ;DECREMENT COUNTER
BNE LOOP ;CONTINUE UNTIL COUNTER
EXIT:
RTS
; DATA
RETADR .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SAMPLE EXECUTION:
SC0606:
256 ARITHMETIC
LDA AYlADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH SIZE OF ARRAYS
JSR MPBADD ;MULTIPLE-PRECISION BINARY ADDITION
BRK ;RESULT OF 1234567H + 1234567H = 24
; IN MEMORY AY1 = CEH
AY 1+1 = 8AH
; AY1+2 = 46H
AY1+3 = 02H
AY 1+4 = 00H
AY1+5 00H
AY1+6 00H
JMP SC0606
AY1:
.BYTE 067H
.BYTE 045H
.BYTE 023H
• BYTE 001H
.BYTE 0
.BYTE 0
.BYTE 0
AY2:
.BYTE 067H
.BYTE 045H
.BYTE 023H
.BYTE 001H
.BYTE 0
.BYTE 0
. BYTE 0
.END :PROGRAM
Multiple-Precision Binary Subtraction
(MPBSUB) 6G
257
258 ARITHMETIC
Example
Data: Length of operands (in bytes) = 4
Minuend = 2F5BA7C316
Subtrahend = 14DF35B816
;EQUATES
MINPTR: .EQU 0D0H ;PAGE ZERO FOR MINUEND POINTER
SUBPTR: .EQU 0D2H ;PAGE ZERO FOR SUBTRAHEND POINTER
6G MULTIPLE-PRECISION BINARY SUBTRACTION (MPBSUB) 259
MPBSUB:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
INITIALIZE
LDY #0
CPX #0 ;IS LENGTH OF ARRAYS = 0 ?
BEQ EXIT ;YES, EXIT
SEC ;SET CARRY
LOOP:
LDA (MINPTR),Y ;GET NEXT BYTE
SBC (SUBPTR),Y ;SUBTRACT BYTES
STA (MINPTR),Y ;STORE DIFFERENCE
INY ;INCREMENT ARRAY INDEX
DEX ;DECREMENT COUNTER
BNE LOOP ;CONTINUE UNTIL COUNTER
EXIT:
RTS
;DATA
RETADR .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
;
SAMPLE EXECUTION:
;
4LOU ARITHMETIC
SC0607:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH SIZE OF ARRAYS
JSR MPBSUB ;MULTIPLE-PRECISION BINARY SUBTRACTION
BRK ;RESULT OF 7654321H - 1234567H = 641FDBAH
IN MEMORY AY1 = OBAH
AY 1+1 = OFDH
; AY1+2 = 41H
; AY1+3 = (X6H
AY 1+4 = 00H
AY1+5 = 00H
; AY1+6 = 00H
JMP SC0607
AY1:
• BYTE 021H
.BYTE 043H
.BYTE 065H
.BYTE 007H
.BYTE 0
.BYTE 0
.BYTE 0
AY2:
.BYTE 067H
.BYTE 045H
.BYTE 023H
.BYTE 001H
.BYTE 0
.BYTE 0
.BYTE 0
.END ;PROGRAM
Multiple-Precision Binary Multiplication
(MPBMUL) 6H
Multiplies two multi-byte unsigned bi time it finds a 1 bit in the multiplier (array 1).
nary numbers. Both numbers are stored with The partial product and the multiplier are
their least significant byte at the lowest shifted through the bit length plus 1 with the
address. The product replaces one of the extra loop being necessary to move the final
numbers (the one with the starting address carry into the product. The program main
lower in the stack). The length of the num tains a full double-length unsigned partial
bers (in bytes) is 255 or less. Only the least product in memory locations starting at
significant bytes of the product are returned HIPROD (more significant bytes) and in
to retain compatibility with other multiple- array 1 (less significant bytes). The less sig
precision binary operations. nificant bytes of the product replace the
Procedure: The program uses an ordinary multiplier as the multiplier is shifted and
add-and-shift algorithm, adding the multipli examined for 1 bits. A length of 00 causes an
cand (array 2) to the partial product each exit with no multiplication.
Registers Used: All anywhere in RAM are temporary storage for the
more significant bytes of the product (255 bytes
Execution Time: Depends on the length of the
starting at address HIPROD), the return address
operands and on the number of 1 bits in the
multiplier (requiring actual additions). If the
(two bytes starting at address RETADR), the
average number of 1 bits in the multiplier is four
loop counter (two bytes starting at address
per byte, the execution time is approximately COUNT), and the length of the operands in bytes
(one byte at address LENGTH). The four bytes
316 x LENGTH2 + 223 x LENGTH + 150 on page 0 hold pointers to the two operands (the
cycles where LENGTH is the number of bytes in pointers start at addresses AY1PTR and
the operands. If, for example, LENGTH = 4, the AY2PTR, respectively). In the listing, AY1PTR
approximate execution time is is taken as address 00D0l6 and AY2PTR as
address 00D216.
316 x 42 + 223 x 4 + 150 = 316 x 16 4- 892
+ 150 = 5056 + 1042 - 6,098 cycles.
Special Case: A length of zero causes an
Program Size: 145 bytes immediate exit with the product equal to the orig
inal multiplier (that is, array 1 is unchanged) and
Data Memory Required: 260 bytes anywhere in the more significant bytes of the product (starting
RAM plus four bytes on page 0. The 260 bytes at address HIPROD) undefined.
261
262 ARITHMETIC
Example
Note that MPBMUL returns only the less
Data: Length of operands (in bytes) = 04 significant bytes (that is, the number of bytes
Top operand (array 2 or multiplicand) in the multiplicand and multiplier) of the
= 0005DlF716 = 381>43110 product to maintain compatibility with other
EQUATES
AY1PTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 1 POINTER
AY2PTR: .EQU 0D2H ;PAGE ZERO FOR ARRAY 2 POINTER
MPBMUL:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1 ;SAVE RETURN ADDRESS
SRPLP:
ROR HIPROD-1,X ;MINUS 1 FOR INDEXING FROM 1 TO LENGTH
DEX
BNE SRPLP ;CONTINUE UNTIL INDEX = 0
;SHIFT CARRY WHICH IS THE NEXT BIT OF LOWER PRODUCT INTO THE MOST
? SIGNIFICANT BIT OF ARRAY 1. THIS IS THE NEXT BIT OF THE PRODUCT.
; THIS ALSO SHIFTS THE NEXT BIT OF MULTIPLIER TO CARRY.
LDY LENGTH
SRA1LP:
LDA (AY1PTR) ,Y
ROR A ;ROTATE NEXT BYTE
STA (AY1PTR) ,Y
DEY
BNE SRA1LP ;CONTINUE UNTIL INDEX = 0
EXIT:
RTS
;DATA
SAMPLE EXECUTION:
SC0608:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH SIZE OF ARRAYS
JSR MPBMUL ;MULTIPLE-PRECISION BINARY MULTIPLY
BRK ;RESULT OF 12345H * 1234H = 14B60404H
; IN MEMORY AY1 = 04H
AY1+1 = 04H
AY1+2 = B6H
AY1+3 = 14H
; AY1+4 = 00H
? AY1+5 o 00H
AY1+6 ■ 00H
JMP SC0608
AY1:
.BYTE 045H
.BYTE 023H
.BYTE 001H
.BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
AY2:
.BYTE 034H
.BYTE 012H
.BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
• END ;PROGRAM
Multiple-Precision Binary Division (MPBDIV) 61
Divides two multi-byte unsigned binary Procedure: The program performs division
numbers. Both numbers are stored with their by the usual shift-and-subtract algorithm,
least significant byte at the lowest address. shifting quotient and dividend and placing a 1
The quotient replaces the dividend (the bit in the quotient each time a trial subtrac
operand with the starting address lower in the tion is successful. An extra buffer is used to
stack). The length of the numbers (in bytes) hold the result of the trial subtraction and
is 255 or less. The remainder is not returned, that buffer is simply switched with the buffer
but its starting address (least significant byte) holding the dividend if the trial subtraction is
is available in memory locations HDEPTR successful. The program exits immediately,
and HDEPTR +1. The Carry flag is cleared if setting the Carry flag, if it finds the divisor to
no errors occur; if a divide by zero is be zero. The Carry flag is cleared otherwise.
attempted, the Carry flag is set to 1, the divi
dend is left unchanged, and the remainder is
set to zero.
Registers Used: All RETADR), the loop counter (two bytes starting
at address COUNT), the length of the operands
Execution Time: Depends on the length of the
(one byte at address LENGTH), and the
operands and on the number of 1 bits in the quo
tient (requiring a buffer switch). If the average addresses of the high dividend buffers (two bytes
starting at address AHIDE1 and two bytes start
number of 1 bits in the quotient is four per byte,
the execution time is approximately
ing at address AHIDE2). The eight bytes on page
0 hold pointers to the two operands and to the
480 x LENGTH2 + 438 x LENGTH + 208 two temporary buffers for the high dividend. The
pointers start at addresses AY1PTR (00D016 in
cycles where LENGTH is the number of bytes in
the listing), AY2PTR (00D216 in the listing),
the operands. If, for example, LENGTH = 4 (32-
bit division), the approximate execution time is HDEPTR (00D416 in the listing), and ODEPTR
(00D616 in the listing). HDEPTR contains the
480 x 42 + 438 x 4 + 208 = address of the least significant byte of the
480 x 16 + 1752 + 208 = remainder at the conclusion of the program.
7680 + 1960 = 9,640 cycles
Special Cases:
Program Size: 206 bytes 1. A length of zero causes an immediate exit
with the Carry flag cleared, the quotient equal to
Data Memory Required: 519 bytes anywhere in
the original dividend, and the remainder
RAM plus eight bytes on page 0. The 519 bytes
undefined.
anywhere in RAM are temporary storage for the
high dividend (255 bytes starting at address 2. A divisor of zero causes an exit with the
HIDED, the result of the trial subtraction (255 Carry flag set to 1, the quotient equal to the origi
bytes starting at address HIDE2), the return nal dividend, and the remainder equal to zero.
address (two bytes starting at address
267
268 ARITHMETIC
Example
Data: Length of operands (in bytes) = 03
Top operand (array 2 or divisor) --= 000F4516 = 3,90910
Bottom operand (array 1 or dividend) = 35A2F716 « 3,515,12710
Result: Bottom operand (array 1) = Bottom
operand (array 1) / Top operand (array 2)
= 00038316 = 89910
Remainder (starting at address in
HDEPTR and HDEPTR+1) = 0003A816
- 936l0
Carry flag is 0 to indicate no
divide by zero error
61 MULTIPLE-PRECISION BINARY DIVISION (MPBDIV) 269
;EQUATES
AY1PTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 1 POINTER
AY2PTR: .EQU 0D2H ;PAGE ZERO FOR ARRAY 2 POINTER
HDEPTR: .EQU 0D4H ;PAGE ZERO FOR HIGH DIVIDEND POINTER
ODEPTR: .EQU 0D6H ;PAGE ZERO FOR OTHER HIGH DIVIDEND POINTER
MPBDIV:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
270 ARITHMETIC
;INITIALIZE
LDA LENGTH ;IS LENGTH OF ARRAYS = 0 ?
BNE INIT
JMP OKEXIT ;YES, EXIT
STA HDEPTR
LDA AHIDE1+1
STA HDEPTR+1
;SHIFT THE CARRY INTO THE LEAST SIGNIFICANT BIT OF THE UPPER DIVIDEND
SLUPR:
LDX LENGTH
LDY #0
SLLP2:
LDA (HDEPTR)', Y
ROL A
272 ARITHMETIC
STA (HDEPTR),Y
INY INCREMENT INDEX
DEX
BNE SLLP2 ;CONTINUE UNTIL REGISTER X = 0
;SUBTRACT ARRAY 2
; OTHER HIGH DIVID
LDY #0
LDX LENGTH
SEC
SUBLP:
LDA (HDEP.TR),Y
SBC (AY2PTR),Y ;SUBTRACT THE BYTES
STA (ODEPTR),Y ;STORE THE DIFFERENCE
INY ;INCREMENT INDEX
DEX
BNE SUBLP ;CONTINUE UNTIL REGISTER X = 0
;IF NO CARRY IS GENERATED FROM THE SUBTRACTION THEN THE HIGH DIVIDEND
; IS LESS THAN ARRAY 2 SO THE NEXT BIT OF THE QUOTIENT IS 0.
; IF THE CARRY IS SET THEN THE NEXT BIT OF THE QUOTIENT IS 1
; AND WE REPLACE DIVIDEND WITH REMAINDER BY SWITCHING POINTERS.
BCC LOOP ;WAS TRIAL SUBTRACTION SUCCESSFUL ?
LDY HDEPTR ;YES, EXCHANGE POINTERS THUS REPLACING
LDX HDEPTR+1 ; DIVIDEND WITH REMAINDER
LDA ODEPTR
STA HDEPTR
LDA ODEPTR+1
STA HDEPTR+1
STY ODEPTR
STX ODEPTR+1
EXIT:
;ARRAY 1 IS THE QUOTIENT
;HDEPTR CONTAINS THE ADDRESS OF THE REMAINDER
RTS
; DATA
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
COUNT: .BLOCK 2 ;TEMPORARY FOR LOOP COUNTER
LENGTH: .BLOCK 1 ;LENGTH OF ARRAYS
61 MULTIPLE-PRECISION BINARY DIVISION (MPBDIV) 273
SAMPLE EXECUTION:
SC0609:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH SIZE OF ARRAYS
JSR MPBDIV ;MULTIPLE-PRECISION BINARY DIVIDE
BRK ;RESULT OF 14B60404H / 1234H = 12345H
; IN MEMORY AY1 = 4 5H
AY1+1 = 23H
AY1+2 = 01H
AY1+3 = 00H
AY1+4 = 0 0H
AY1+5 = 00H
7 AY1+6 = 0 0H
JMP SC0609
AY1:
.BYTE 004H
.BYTE 004H
.BYTE 0B6H
.BYTE 014H
.BYTE 0
.BYTE 0
.BYTE 0
&Y2:
.BYTE 034H
.BYTE 012H
.BYTE 0
.BYTE 0
274 ARITHMETIC
.BYTE 0
.BYTE 0
.BYTE 0
.END ;PROGRAM
Multiple-Precision Binary Comparison
(MPBCMP) 6J
operands one byte at a time, starting with the Data Memory Required: Two bytes anywhere in
RAM and four bytes on page 0. The two bytes
most significant bytes and continuing until it
anywhere in RAM are temporary storage for the
finds corresponding bytes that are not equal. return address (starting at address RETADR).
If all the bytes are equal, it exits with the Zero The four bytes on page 0 hold pointers to the two
numbers; the pointers start at addresses
flag set to 1. Note that the comparison works
MINPTR (00D016 in the listing) and SUBPTR
through the operands starting with the most (00D216 in the listing).
significant bytes, whereas the subtraction Special Case: A length of zero causes an
(Subroutine 6G) starts with the least signifi immediate exit with the Carry flag and the Zero
cant bytes. flag both set to 1.
Less significant byte of return address Flags set as if subtrahend had been
More significant byte of return address subtracted from minuend
Length of the operands in bytes Zero flag = 1 if subtrahend and minuend are
Less significant byte of starting address of equal, 0 if they are not equal
subtrahend (address containing the least Carry flag = 0 if subtrahend is larger than
significant byte) minuend in the unsigned sense, 1 if it is less
More significant byte of starting address of than or equal to the minuend.
subtrahend (address containing the least
significant byte)
Less significant byte of starting address of
minuend (address containing the least sig-
275
276 ARITHMETIC
nificant byte)
More significant byte of starting address of
minuend (address containing the least sig
nificant byte)
Examples
1. Data: Length of operands (in bytes) = 6 3. Data: Length of operands (in bytes) = 6
Top operand (subtrahend) = Top operand (subtrahend) =
19D028A193EA16 19D028A193EA16
Bottom operand (minuend) = Bottom operand (minuend) =
4E67BC15A26616 0F37E5991D7C16
Result: Zero flag = 0 (operands are Result: Zero flag = 0 (operands are not equal)
not equal) Carry flag = 0 (subtrahend is larger
Carry flag = 1 (subtrahend is than minuend)
not larger than minuend)
2. Data: Length of operands (in bytes)
= 6
Top operand (subtrahend) =
19D028A193EA16
Bottom operand (minuend) =
19D028A193EA16
;EQUATES
MINPTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 1 POINTER
SUBPTR: .EQU 0D2H ;PAGE ZERO FOR ARRAY 2 POINTER
MPBCMP:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1 ;SAVE RETURN ADDRESS
;INITIALIZE
CPY #0 ;IS LENGTH OF ARRAYS = 0 ?
BEQ EXIT ;YES, EXIT WITH C=1,Z=1
LOOP:
LDA (MINPTR),Y ;GET NEXT BYTE
CMP (SUBPTR),Y ;COMPARE BYTES
BNE EXIT ;EXIT IF THEY ARE NOT EQUAL, THE FLAGS ARE SET
DEY ;DECREMENT INDEX
BNE LOOP ;CONTINUE UNTIL COUNTER = 0
; IF WE FALL THROUGH THEN THE ARRAYS ARE EQUAL
; AND THE FLAGS ARE SET PROPERLY
EXIT:
RTS
; DATA
RETADR .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SAMPLE EXECUTION:
SC0610:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH SIZE OF ARRAYS
JSR MPBCMP ;MULTIPLE-PRECISION BINARY COMPARISON
BRK ;RESULT OF COMPARE(7654321H,1234567H) IS
; C=1,Z=O
JMP SC0610
AY1:
6J MULTIPLE-PRECISION BINARY COMPARISON (MPBCMP) 279
BYTE 021H
BYTE 043H
BYTE 065H
BYTE 007H
BYTE 0
BYTE 0
BYTE 0
AY2:
BYTE 067H
BYTE 045H
BYTE 023H
BYTE 001H
BYTE 0
BYTE 0
BYTE 0
END ;PROGRAM
Multiple-Precision Decimal Addition
(MPDADD) 6K
280
6K MULTIPLE-PRECISION DECIMAL ADDITION (MPDADD) 281
Example
Data: Length of operands (in bytes) = 6
Top operand (array 2) = 19602881931516
Bottom operand (array 1) =
29347160598716
;
;EQUATES
AY1PTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 1 POINTER
AY2PTR: .EQU 0D2H ;PAGE ZERO FOR ARRAY 2 POINTER
MPDADD:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
LOOP:
LDA (AY1PTR) ,Y ;GET NEXT BYTE
ADC (AY2PTR) ,Y ;ADD BYTES
STA (AY1PTR) ,Y ;STORE SUM
INY ;INCREMENT ARRAY INDEX
DEX ;DECREMENT COUNTER
BNE LOOP ;CONTINUE UNTIL COUNTER
EXIT:
CLD ;RETURN IN BINARY MODE
6K MULTIPLE-PRECISION DECIMAL ADDITION (MPDADD) 283
RTS
; DATA
RETADR .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SAMPLE EXECUTION:
SC0611:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH SIZE OF ARRAYS
JSR MPDADD ;MULTIPLE-PRECISION BCD ADDITION
BRK ;RESULT OF 1234567 + 1234567 = 2469134
IN MEMORY AY1 = 34H
AY 1+1 = 91H
AY 1+2 = 46H
AY1+3 = 02H
AY 1+4 = 00H
AY1+5 = 00H
AY 1+6 = 00H
JMP SC0611
AY1:
.BYTE 067H
.BYTE 045H
..BYTE 023H
.BYTE 001H
.BYTE 0
.BYTE 0
.BYTE 0
AY2:
.BYTE 067H
.BYTE 045H
.BYTE 023H
284 ARITHMETIC
.BYTE 001H
.BYTE 0
.BYTE 0
.BYTE 0
.END ;PROGRAM
Multiple-Precision Decimal Subtraction
(MPDSUB) 6L
minuend (number from which the Data Memory Required: Two bytes anywhere in
RAM and four bytes on page 0. The two bytes
subtrahend is subtracted). The difference
anywhere in RAM are temporary storage for the
replaces the minuend in memory. The length return address (starting at address RETADR).
of the numbers (in bytes) is 255 or less. The The four bytes on page 0 hold pointers to the two
operands; the pointers start at addresses
program returns with the Decimal Mode (D)
AY1PTR (00D016 in the listing) and AY2PTR
flag cleared (binary mode). (00D216 in the listing).
Procedure: The program first enters the Special Case: A length of zero causes an
decimal mode by setting the D flag. It then immediate exit with the difference equal to the
sets the Carry flag (the inverted borrow) original minuend, the Decimal Mode flag cleared
(binary mode), and the Carry flag set to 1.
initially and subtracts the subtrahend from
the minuend one byte (two digits) at a time,
starting with the least significant digits. The
final Carry flag reflects the subtraction of the the listing). A length of 00 causes an immedi
most significant digits. The difference re ate exit with no subtraction operations. The
places the minuend (the operand with the program clears the D flag (thus placing the
starting address lower in the stack, array 1 in processor in the binary mode) before exiting.
285
286 ARITHMETIC
Example
Data: Length of operands (in bytes) = 6
Minuend (array 1) = 29347160598716
Subtrahend (array 2) = 19602881931516
;EQUATES
MINPTR: .EQU 0D0H ;PAGE ZERO FOR MINUEND POINTER
SUBPTR: .EQU 0D2H ;PAGE ZERO FOR SUBTRAHEND POINTER
MPDSUB:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;INITIALIZE
LDY #0
CPX #0 ;IS LENGTH OF ARRAYS = 0
BEQ EXIT ;YES, EXIT
SED ;SET DECIMAL MODE
SEC ;SET CARRY
LOOP:
LDA (MINPTR) ,Y ;GET NEXT BYTE
SBC (SUBPTR) ,Y ;SUBTRACT BYTES
§TA (MINPTR) ,Y ;STORE DIFFERENCE
INY ;INCREMENT ARRAY INDEX
DEX ;DECREMENT COUNTER
BNE LOOP ;CONTINUE UNTIL COUNTER
288 ARITHMETIC
EXIT:
CLD ;RETURN IN BINARY MODE
RTS
; DATA
RETADR .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
SAMPLE EXECUTION:
SC0612:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA PUSH SIZE OF ARRAYS
JSR MPDSUB MULTIPLE-PRECISION BCD SUBTRACTION
BRK ;RESULT OF 2469134 - 1234567 = 1234567
IN MEMORY AY1 = 67H
AY 1+1 = 45H
AY 1+2 = 23H
AY1+3 = 01H
AY 1+4 = 00H
AY1+5 = 00H
AY 1+6 = 00H
JMP SC0612
AY1:
.BYTE 034H
.BYTE 091H
.BYTE 04 6H
.BYTE 002H
.BYTE 0
.BYTE 0
.BYTE 0
6L MULTIPLE-PRECISION DECIMAL SUBTRACTION (MPDSUB) 289
AY2:
.BYTE 067H
.BYTE 045H
.BYTE 023H
.BYTE 001H
.BYTE 0
.BYTE 0
.BYTE 0
.END ;PROGRAM
Multiple-Precision Decimal Multiplication
(MPDMUL) 6M
290
6M MULTIPLE-PRECISION DECIMAL MULTIPLICATION (MPDMUL) 291
;EQUATES
AY1PTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 1 POINTER
AY2PTR: .EQU 0D2H ;PAGE ZERO FOR ARRAY 2 POINTER
MPDMUL:
;SAVE RETURN ADDRESS
PLA
STA RETADR
PLA
STA RETADR+1
;INITIALIZE
SED ;PUT PROCESSOR IN DECIMAL MODE
LDY #0
LDX LENGTH ;IS LENGTH ZERO ?
BNE JNITLP
JMP EXIT ;YES, EXIT
LDA #0
STA (AY1PTR),Y ;ZERO ARY1[Y]
STA PROD,Y ;ZERO PROD
INY
DEX ;DECREMENT LOOP COUNTER
BNE INITLP ;CONTINUE UNTIL DONE
SHFTLP:
DEY ;DECREMENT Y SO IT POINTS AT THE NEXT BYTE
LDA PROD,Y
PHA ;SAVE LOW DIGIT OF PROD,Y
AND #0F0H ;CLEAR LOW DIGIT
EXIT:
6M MULTIPLE-PRECISION DECIMAL MULTIPLICATION (MPDMUL) 295
•DATA
RETADR: .BLOCK 2 ;TEMPORARY FOR RETURN ADDRESS
Length: .BLOCK 1 ;LENGTH OF ARRAYS
NDIGIT: .BLOCK 1 ;NEXT DIGIT
IN ARRAY
DCNT: .BLOCK 1 ;DIGIT COUNTER FOR BYTES IN ARRAYS
IDX: .BLOCK 1 ;BYTE INDEX INTO ARRAYS
OVRFLW: .BLOCK 1 ;OVERFLOW BYTE
PROD: .BLOCK 255 ;PRODUCT BUFFER
MCAND: .BLOCK 255 ;MULTIPLICAND BUFFER
SAMPLE EXECUTION:
SC0613:
LDA AY1ADR+1
PHA
LDA AY1ADR
PHA ;PUSH AYl ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ;PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH LENGTH OF ARRAYS
JSR MPDMUL ;MULTIPLE-PRECISION BCD MULTIPLICATION
BRK ;RESULT OF 1234 * 1234 = 1522756
; IN MEMORY AYl = 56H
AY1+1 = 27H
AYl+2 = 52H
AY1+3 = 01H
AYl+4 = 0 0H
AYl+5 = 00H
; AYl+6 = 0 0H
JMP SC0613
AYl:
.BYTE 034H
.BYTE 012H
.BYTE 0
.BYTE 0
296 ARITHMETIC
.BYTE 0
.BYTE 0
.BYTE 0
AY2:
.BYTE 034H
.BYTE 012H
• BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
.END ;PROGRAM
Multiple-Precision Decimal Division
(MPDDIV) 6N
Divides two multi-byte unsigned decimal The program returns with the Decimal Mode
numbers. Both numbers are stored with their (D) flag cleared (binary mode).
least significant byte at the lowest address. Procedure: The program performs division
The quotient replaces the dividend (the by trial subtractions, a digit at a time. It deter
operand with the starting address lower in the mines how many times the divisor can be
stack). The length of the numbers (in bytes) subtracted from the dividend and then saves
is 255 or less. The remainder is not returned that number in the quotient and makes the
but the address of its least significant byte is remainder into the new dividend. It then
available starting at memory location rotates the dividend and the quotient left one
HDEPTR. The Carry flag is cleared if no digit. The program exits immediately, setting
errors occur; if a divide by zero is attempted, the Carry flag, if it finds the divisor to be
the Carry flag is set to 1, the dividend is left zero. The Carry flag is cleared otherwise.
unchanged, and the remainder is set to zero.
297
298 ARITHMETIC
Example
Data: Length of operands (in bytes) = 04 Result: Bottom operand (array 1) = Bottom
Top operand (array 2 or divisor) = operand (array 1)/Top operand
0000629416 (array 2) = 00003518l6
;EQUATES
AY2PTR: .EQU 0D0H ;PAGE ZERO FOR ARRAY 2 (DIVISOR) POINTER
HDEPTR: .EQU 0D2H ;PAGE ZERO WHICH POINTS TO THE CURRENT
; HIGH DIVIDEND POINTER
ODEPTR: .EQU 0D4H
;PAGE ZERO WHICH POINTS TO THE OTHER
; HIGH DIVIDEND POINTER
RLPTR: .EQU 0D6H
;PAGE ZERO FOR ROTATE LEFT ARRAY
MPDDIV:
300 ARITHMETIC
;INITIALIZE
CLD ;PUT PROCESSOR INTO BINARY MODE
;NDIGIT := 0
LDA #0
STA NDIGIT
CMP #0
BNE DVLOOP ;BRANCH IF DIVISOR IS NOT 0
JMP EREXIT ;ERROR EXIT
;ROTATE LEFT THE HIGH DIVIDEND WHERE THE LEAST SIGNIFICANT DIGIT
; OF HIGH DIVIDEND BECOMES THE HIGH DIGIT OF NDIGIT
ROLDVB:
LDA HDEPTR+1
LDY HDEPTR
JSR RLARY
302 ARITHMETIC
;SEE HOW MANY TIMES THE DIVISOR WILL GO INTO THE HIGH DIVIDEND
; WHEN WE EXIT FROM THIS LOOP THE HIGH DIGIT OF NDIGIT IS THE NEXT
; QUOTIENT DIGIT AND HIGH DIVIDEND IS THE REMAINDER
LDA #0
STA NDIGIT ;NDIGIT := 0
SED ;ENTER DECIMAL MODE
SUBLP:
LDY #0 ;Y = INDEX INTO ARRAYS
LDX LENGTH ;X = LENGTH
SEC ;SET INVERTED BORROW
INNER:
LDA (HDEPTR),Y ;GET NEXT BYTE OF DIVIDEND
SBC (AY2PTR),Y ;SUBTRACT BYTE OF DIVISOR
STA (ODEPTR),Y ;SAVE DIFFERENCE FOR NEXT SUBTRACTION
INY ;INCREMENT ARRAY INDEX
DEX ;DECREMENT LOOP COUNTER
BNE INNER ;CONTINUE THROUGH ALL THE BYTES
BCC DVLOOP ;BRANCH WHEN BORROW OCCURS AT WHICH TIME
; NDIGIT IS THE NUMBER OF TIMES THE DIVISOR
; GOES INTO THE ORIGINAL HIGH DIVIDEND AND
; HIGH DIVIDEND CONTAINS THE REMAINDER.
EREXIT:
SEC
EXIT:
;HDEPTR CONTAINS THE ADDRESS OF THE REMAINDER
CLD ;RETURN IN BINARY MODE
RTS
6N MULTIPLE-PRECISION DECIMAL DIVISION (MPDDIV) 303
.a**********************************
;SUBROUTINE: RLARY
;PURPOSE: ROTATE LEFT AN ARRAY ONE DIGIT (4 BITS)
;ENTRY: A = HIGH BYTE OF ARRAY ADDRESS
; Y = LOW BYTE OF ARRAY ADDRESS
; THE HIGH DIGIT OF NDIGIT IS THE DIGIT TO ROTATE THROUGH
;EXIT: ARRAY ROTATED LEFT THROUGH THE HIGH DIGIT OF NDIGIT
;REGISTERS USED: ALL
***********************************
RLARY:
;STORE ARRAY ADDRESS
STA RLPTR+1
STY RLPTR
SHIFT:
LDA (RLPTR),Y ;GET NEXT BYTE
PHA ;SAVE HIGH DIGIT
AND #0FH ;CLEAR HIGH DIGIT
ASL NDIGIT
ORA NDIGIT ;BITS 0..3 = LOW DIGIT OF ARRAY
;BITS 5..7 AND CARRY = NEXT DIGIT
ROL A
ROL A
ROL A
ROL A ;NOW NDIGIT IN BITS 0..3 AND
; LOW DIGIT IN HIGH DIGIT
STA (RLPTR),Y ;STORE IT
PLA ;GET OLD HIGH DIGIT
AND #0F0H ;CLEAR LOWER DIGIT
STA NDIGIT ;STORE IN NDIGIT
INY ;INCREMENT TO NEXT BYTE
DEX ;DECREMENT COUNT
BNE SHIFT ;BRANCH IF NOT DONE
RTS
;DATA
SAMPLE EXECUTION:
SC0614:
LDA AYlADR+1
PHA
LDA AY1ADR
PHA ;PUSH AY1 ADDRESS
LDA AY2ADR+1
PHA
LDA AY2ADR
PHA ?PUSH AY2 ADDRESS
LDA #SZAYS
PHA ;PUSH LENGTH OF ARRAYS
JSR MPDDIV ;MULTIPLE-PRECISION BCD DIVISION
BRK ;RESULT OF 1522756 / 1234 = 1234
IN MEMORY AY1 = 34H
AY 1+1 = 12H
AY 1+2 = OOH
AY 1+3 = OOH
AY 1+4 = OOH
AY1+5 = OOH
AY 1+6 = OOH
JMP SC0614
AY1:
.BYTE 056H
.BYTE 027H
.BYTE 052H
.BYTE 01H
.BYTE 0
.BYTE 0
.BYTE 0
AY2:
.BYTE 034H
.BYTE 012H
.BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
.BYTE 0
.END ;PROGRAM
Multiple-Precision Decimal Comparison 60
Compares two multi-byte unsigned if the subtrahend had been subtracted from
decimal (BCD) numbers and sets the Carry the minuend.
and Zero flags appropriately. The Zero flag is Note: This program is exactly the same as
set to 1 if the operands are equal and to 0 if Subroutine 6J, the multiple-precision binary
they are not equal. The Carry flag is set to 0 if comparison, since the CMP instruction oper
the operand with the address higher in the ates the same in the decimal mode as in the
stack (the subtrahend) is larger then the binary mode. Hence, see Subroutine 6J for a
other operand (the minuend); the Carry flag listing and other details.
is set to 1 otherwise. Thus the flags are set as
Examples
1. Data: Length of operands (in bytes) = 6 3. Data: Length of operands (in bytes) = 6
Top operand (subtrahend) = Top operand (subtrahend) =
19652871934016 19652871934016
Bottom operand (minuend) = Bottom operand (minuend) =
45678015326616 073785991074I6
Result: Zero flag = 0 (operands are not equal) Result: Zero flag = 0 (operands are not equal)
Carry flag = 1 (subtrahend is not Carry flag = 0 (subtrahend is larger
larger than minuend) than minuend)
2. Data: Length of operands (in bytes) = 6
Top operand (subtrahend) =
19652871934016
Bottom operand (minuend) =
19652871934016
Result: Zero flag = 1 (operands are equal)
Carry flag = 1 (subtrahend is not
larger than minuend)
305
Bit Set (BITSET) 7A
More significant byte of data in accumulator More significant byte of result in accumulator
Less significant byte of data in register Y Less significant byte of result in register Y
Examples
1. Data: (A) = ^ = 011011102 2. Data: (A) = 6E1(| = 011011102
(more significant byte) (more significant byte)
(Y)«3916 = 001110012 (Y) = 3916 = 001110012
(less significant byte) (less significant byte)
(X) = 0C16 = 1210 (X) - 0216 - 2l0
(bit position to set) (bit position to set)
306
7A (BITSET) BIT SET 307
Time: 57 cycles
BITSET:
;SAVE THE DATA WORD
STA VALUE+1
STY VALUE
LDA VALUE+1
LDY VALUE
RTS
308 BIT MANIPULATIONS AND SHIFTS
; DATA
VALUE: .BLOCK 2 ;TEMPORARY FOR THE DATA WORD
SAMPLE EXECUTION
SC0701:
LDA VAL+1 ;LOAD DATA WORD INTO A,Y
LDY VAL
LDX BITN ;GET BIT NUMBER IN X
JSR BITSET ;SET THE BIT
BRK ;RESULT OF VAL « 5555H AND BITN = OF
; REGISTER A = D5H, REGISTER Y = 55H
JMP SC0701
.END ;PROGRAM
Bit Clear (BITCLR) 7B
More significant byte of data in accumulator More significant byte of result in accumulator
Less significant byte of data in register Y Less significant byte of result in register Y
Bit number to clear in register X
Examples
1. Data: (A) - 6E,6 - 011011102 2. Data: (A) = 6E,fi«0110111016
(more significant byte) (more significant byte)
(Y) = 3916 = 0011100116 (Y) = 3916-001110012
(less significant byte) (less significant byte)
(X) = 0E16 = 14IO (X) - 04,6 - 410
(bit position to clear) (bit position to clear)
Result: (A) = 2E,6 = 011011102 Result: 16
(more significant byte, bit 14 cleared) (more significant byte)
(Y) =3916 = 001110012 (Y) = 2916 - 001010012
(less significant byte) (less significant byte, bit 4 cleared)
309
310 BIT MANIPULATIONS AND SHIFTS
BITCLR:
;SAVE THE DATA WORD
STA VALUE+1
STY VALUE
LDA VALUE+1
LDY VALUE.
RTS
7B (BITCLR) BIT CLEAR 311
; DATA
VALUE: .BLOCK 2 ;TEMI>ORARY
SAMPLE EXECUTION
SC0702:
LDA VAL+1 ;LOAD DATA WORD INTO A,Y
LDY VAL
LDX BITN ;GET BIT NUMBER IN X
JSR BITCLR ;CLEAR THE BIT
BRK ;RESULT OF VAL = 5555H AND BITN = 00H IS
; REGISTER A = 55H, REGISTER Y = 54H
JMP SC0702
• END ;PROGRAM
Bit Test (BITTST) 7C
More significant byte of data in accumulator Carry set to value of specified bit position in
Less significant byte of data in register Y data.
Examples
1. Data: (A) = 6E16 = 011011102 2. Data:
(more significant byte) () 16
(more significant byte)
(Y) = 3916 = 001110012
(less significant byte) (Y) = 39l6 = 001110012
(less significant byte)
(X) = 0B16=ll10
(bit position to test) (X) = 0616 = 610
(bit position to test)
312
7C (BITTST) BIT TEST 313
BITTST:
;SAVE THE DATA WORD
STA VALUE+1
STY VALUE
; DATA
VALUE: .BLOCK 2 .•TEMPORARY FOR THE DATA WORD
SAMPLE EXECUTION
SC0703:
LDA VAL+1 ;LOAD DATA WORD INTO A,Y
LDY VAL
LDX BITN ;GET BIT NUMBER IN X
JSR BITTST •TEST THE BIT
BRK ;RESULT OF VAL = 5555H AND BITN = 01 IS
;CARRY = 0
JMP SC0703
.END ;PROGRAM
Bit Field Extraction (BFE) 7D
Extracts a field of bits from a word and table, shifts the mask left to align it with the
returns the field in the least significant bit specified starting bit position, and obtains the
positions. The width of the field and its start field by logically ANDing the mask and the
ing bit position are specified. data. It then normalizes the bit field by shift
Procedure: The program obtains a mask ing it right so that it starts in bit 0.
with the specified number of 1 bits from a
Registers Used: All ing at address VALUE), and the mask (two bytes
starting at address MASK).
Execution Time: 34 • STARTING BIT POSI
TION plus 138 cycles overhead. The starting bit Special Cases:
position determines the number of times the 1. Requesting a field that would extend
mask must be shifted left and the bit field right. beyond the end of the word causes the program
For example, if the field starts in bit 6, the execu to return with only the bits through bit 15. That
tion time is is, no wraparound is provided. If, for example,
34 • 6 4- 138 = 204 + 138 = 342 cycles the user asks for a 10-bit field starting at bit 8, the
program will return only 8 bits (bits 8 through 15).
Program Size: 134 bytes
2. Both the starting bit position and the num
Data Memory Required: Six bytes anywhere in ber of bits in the field are interpreted mod 16.
RAM for the index (one byte at address That is, for example, bit position 17 is equivalent
INDEX), the width of the field (one byte at to bit position 1 and a field of 20 bits is equivalent
address WIDTH), the data value (two bytes start to a field of 4 bits.
Examples
1. Data: Value = F67C,6 = 11110110011111002 Result: Bit field = 006716 = 00000000011001112
Starting bit position = 4 We have extracted 8 bits from the original
Number of bits in the field = 8 data, starting with bit 4 (that is, bits
4 through 11).
315
316 BIT MANIPULATIONS AND SHIFTS
;
; Purpose: Extract a field of bits from a 16 bit word and ;
; return the field normalized to bit 0. ;
; NOTE: IF THE REQUESTED FIELD IS TOO LONG, THEN ;
? ONLY THE BITS THROUGH BIT 15 WILL BE ;
RETURNED. FOR EXAMPLE IF A 4 BIT FIELD IS ;
; REQUESTED STARTING AT BIT 15 THEN ONLY 1 ;
; BIT (BIT 15) WILL BE RETURNED. ;
; ?
; Entry: TOP OF STACK ;
• Low byte of return address, ;
; High byte of return address, ;
; Starting (lowest) bit position in the field ;
(0..15),
Number of bits in the field (1..16),
; Low byte of data word, ;
. High byte of data word, ;
; '
. Exit: Register A = High byte of field
. Register Y = Low byte of field ?
BFE:
;GET THE NUMBER OF BITS IN THE FIELD (MAP FROM 1..WIDTH TO 0..WIDTH-1)
PLA
SEC
SBC #1 ; SUBTRACT 1
AND #0FH ;MAKE SURE IT IS 0 TO 15
STA WIDTH ;SAVE WIDTH
EXIT:
LDY VALUE
LDA VALUE+1
RTS
SAMPLE EXECUTION:
SC0704:
LDA VAL+1
PHA
LDA VAL
PHA ;PUSH THE DATA WORD
LDA NBITS
PHA ;PUSH FIELD WIDTH (NUMBER OF BITS)
LDA POS
7D (BFE) BIT FIELD EXTRACTION 319
.END ;PROGRAM
Bit Field Insertion (BFI) 7E
Inserts a field of bits into a word. The width left to align them with the specified starting
of the field and its starting (lowest) bit posi bit position. It logically ANDs the mask and
tion are specified. the original data word, thus clearing the
Procedure: The program obtains a mask required bit positions, and then logically ORs
with the specified number of 0 bits from a the result with the shifted bit field.
table. It then shifts the mask and the bit field
320
7E (BFI) BIT FIELD INSERTION 321
Examples
1. Data: Value = F67C16 = 11110110011111002 2. Data: Value = A2D416 = 10100010110101002
Starting bit position = 4 Starting bit position = 6
Number of bits in the field = 8 Number of bits in the field = 5
Bitfield = 008B16 = 00000000100010112 Bitfield = 001516 - 00000000000101012
BFI:
;SAVE RETURN ADDRESS IN Y,X
PLA
TAY
PLA
TAX
;GET THE NUMBER OF BITS IN THE FIELD (MAP FROM 1..WIDTH TO 0..WIDTH-1)
PLA
SEC
SBC #1 ;SUBTRACT 1
AND #0FH ;MAKE SURE IT IS 0 TO 15
STA WIDTH ;SAVE WIDTH
;SHIFT MASK AND BIT FIELD LEFT INDEX TIMES TO ALIGN THEM
; WITH THE BEGINING OF THE FIELD
LDY INDEX
BEQ INSERT ;BRANCH IF INDEX = 0
SHFTLP:
SEC ;FILL THE MASK WITH ONES
ROL MASK ;ROTATE LOW BYTE SHIFTING A 1 TO BIT 0 AND
; BIT 7 TO CARRY
ROL MASK+1 ;ROTATE HIGH BYTE, BIT 0 := CARRY
ASL INSVAL ;SHIFT THE INSERT VALUE SHIFTING IN ZEROS
ROL INSVAL+1
DEY
BNE SHFTLP ;CONTINUE UNTIL INDEX
;USE THE MASK TO ZERO THE FIELD AND THEN OR IN THE INSERT VALUE
INSERT:
LDA VALUE
AND MASK ;AND LOW BYTE OF VALUE WITH MASK
ORA INSVAL
TAY ;REGISTER Y = LOW BYTE OF THE NEW VALUE
LDA VALUE+1
AND MASK+1 ;AND HIGH BYTE OF VALUE WITH MASK
ORA INSVAL+1 ;REGISTER A = HIGH BYTE OF THE NEW VALUE
;RETURN
RTS
SAMPLE EXECUTION:
SC0705:
LDA VAL+1 ;PUSH THE DATA WORD
PHA
LDA VAL
PHA
LDA VALINS+1 ;PUSH THE VALUE TO INSERT
PHA
LDA VALINS
PHA
LDA NBITS ;PUSH THE FIELD WIDTH
PHA
LDA POS ;PUSH THE STARTING POSITION OF THE FIELD
PHA
JSR BFI ;INSERT
BRK ;RESULT FOR VAL = 1234H, VALINS = OEH,
NBITS = 4, POS = OCH IS
; REGISTER A = E2H, REGISTER Y = 34H
JMP SC0705
.END ;PROGRAM
Multiple-Precision Arithmetic Shift Right
(MPASR) 7F
325
326 BIT MANIPULATIONS AND SHIFTS
Examples
1. Data: Length of operand (in bytes) = 08 2. Data: Length of operand (in bytes) = 04
Operand = 85A4C719FE06741E16 Operand = 3F6A42D316
Number of shifts = 04 Number of shifts = 03
Result: Shifted operand = F85A4C719FE0674116. Result: Shifted operand = 07ED485A16.
This is the original operand shifted right This is the original operand shifted
four bits arithmetically (the four most right three bits arithmetically (the
significant bits thus all take on the value three most significant bits thus all
of the original sign bit, which was 1). take on the value of the original sign
Carry = 1, since the last bit shifted from bit, which was 0).
the rightmost bit position was 1. Garry = 0, since the last bit shifted
from the rightmost bit position was 0.
;EQUATES
PTR: .EQU ODOH ;PAGE ZERO FOR POINTER TO OPERAND
MPASR:
;SAVE RETURN ADDRESS
PLA
TAY
PLA
TAX
INITIALIZE
CLC ;CLEAR CARRY
LDA LENGTH
BEQ EXIT ;EXIT IF LENGTH OF OPERAND IS 0
LDA NBITS
BEQ EXIT ;EXIT IF NUMBER OF BITS TO SHIFT IS 0
; WITH CARRY CLEAR
ASRLP:
LDA MSB ;GET THE MOST SIGNIFICANT BYTE
ASL A ;SHIFT BIT 7 TO CARRY FOR SIGN EXTENSION
LDY LENGTH ;Y = INDEX TO LAST BYTE AND THE COUNTER
LOOP:
LDA (PTR),Y ;GET NEXT BYTE
ROR A ;ROTATE BIT 7 := CARRY, CARRY := BIT 0
STA (PTR),Y •STORE NEW VALUE
DEY ;DECREMENT COUNTER
BNE LOOP ;CONTINUE THROUGH ALL THE BYTES
EXIT:
RTS
;DATA SECTION
NBITS: .BLOCK 1 ;NUMBER OF BITS TO SHIFT
LENGTH: .BLOCK 1 ;LENGTH OF OPERAND IN BYTES
MSB: .BLOCK 1 ;MOST SIGNIFICANT BYTE
SAMPLE EXECUTION:
SC0706:
LDA AYADR+1 ;PUSH STARTING ADDRESS OF OPERAND
PHA
LDA AYADR
PHA
JMP SC0706
;DATA SECTION
SZAY: .EQU 7 ;LENGTH OF OPERAND
SHIFTS: .BYTE 4 ;NUMBER OF SHIFTS
AYADR: .WORD AY ;STARTING ADDRESS OF OPERAND
AY .BYTE 21H/43H,65H,87H,0A9H,0CBH,0EDH
•END ;PROGRAM
Multiple-Precision Logical Shift Left (MPLSL) 7G
Registers Used: All anywhere in RAM are temporary storage for the
number of shifts (one byte at address NBITS)
Execution Time: NUMBER OF SHIFTS ♦ (16 +
and the length of the operand in bytes (one byte
20 * LENGTH OF OPERAND IN BYTES) 4- 73
at address LENGTH). The two bytes on page 0
cycles.
hold a pointer to the operand (starting at address
If, for example, NUMBER OF SHIFTS = 4 PTR, 00D016 in the listing).
and LENGTH OF OPERAND IN BYTES = 6
(i.e., a 4-bit shift of a byte operand) the execution Special Cases:
time is 1. If the length of the operand is zero, the pro
4 * (6 + 20 * 6) + 73 - 4 * (136) + 73 = gram exits immediately with the operand
617 cycles. unchanged and the Carry flag cleared.
2. If the number of shifts is zero, the program
Data Memory Required: Two bytes anywhere in exits immediately with the operand unchanged
RAM plus two bytes on page 0. The two bytes and the Carry flag cleared.
329
330 BIT MANIPULATIONS AND SHIFTS
Examples
1. Data: Length of operand (in bytes) = 08 2. Data: Length of operand (in bytes) = 04
Operand = 85A4C719FE06741E16 Operand - 3F6A42D316
Number of shifts = 04 Number of shifts = 03
Result: Shifted operand = 5A4C719FE06741E016. Result: Shifted operand = FB52169816. This is
This is the original operand shifted the original operand shifted left three
left four bits logically; the four least bits logically; the three least significant
significant bits are all cleared. bits are all cleared.
Carry = 1, since the last bit
Carry = 0, since the last bit shifted from
shifted from the leftmost bit position
the leftmost bit position was 0.
was 1.
;EQUATES
PTR: . EQU ODOH ;PAGE ZERO FOR POINTER TO OPERAND
MPLSL:
;SAVE RETURN ADDRESS
PLA
TAY
PLA
TAX
PLA
STA LENGTH
.•INITIALIZE
CLC ;CLEAR CARRY
LDA LENGTH
BEQ EXIT ;EXIT IF LENGTH OF THE OPERAND IS 0
LDA NBITS
BEQ EXIT ;EXIT IF NUMBER OF BITS TO SHIFT IS 0
; WITH CARRY CLEAR
EXIT:
RTS
;DATA SECTION
NBITS: .BLOCK 1 ;NUMBER OF BITS TO SHIFT
LENGTH: .BLOCK 1 ;LENGTH OF OPERAND
SAMPLE EXECUTION:
SC0707:
LDA AYADR+1 ;PUSH STARTING ADDRESS OF OPERAND
PHA
LDA AYADR
PHA
;DATA SECTION
SZAY: .EQU 7 ;LENGTH OF OPERAND
SHIFTS: .BYTE 4 ;NUMBER OF SHIFTS
AYADR: .WORD AY ;STARTING ADDRESS OF OPERAND
AY: .BYTE 21H/43H,65H,87H,0A9H,0CBH,0EDH
.END ;PROGRAM
Multiple-Precision Logical Shift Right (MPLSR) 7H
Registers Used: All anywhere in RAM are temporary storage for the
number of shifts (one byte at address NBITS)
Execution Time: NUMBER OF SHIFTS * (14 +
and the length of the operand in bytes (one byte
18 ♦ LENGTH OF OPERAND IN BYTES) + 80
at address LENGTH). The two bytes on page 0
cycles.
hold a pointer to the operand (starting at address
If, for example, NUMBER OF SHIFTS = 4 PTR, 00D016 in the listing).
and LENGTH OF OPERAND IN BYTES = 8
(i.e., a 4-bit shift of an 8-byte operand), the
Special Cases:
execution time is
4 * (14 + 18 * 8) + 80 = 4 * (158) + 80 = 1. If the length of the operand is zero, the pro
712 cycles. gram exits immediately with the operand
unchanged and the Carry flag cleared.
Program Size: 59 bytes
2. If the number of shifts is zero, the program
Data Memory Required: Two bytes anywhere in exits immediately with the operand unchanged
RAM plus two bytes on page 0. The two bytes and the Carry flag cleared.
333
334 BIT MANIPULATIONS AND SHIFTS
Examples
1. Data: Length of operand (in bytes) = 08 2. Data: Length of operand (in bytes) = 04
Operand = 85A4C719FE06741E16 Operand = 3F6A42D316
Number of shifts = 04 Number of shifts = 03
? EQUATES
PTR: .EQU ODOH ;PAGE ZERO FOR POINTER TO OPERAND
MPLSR:
;SAVE RETURN ADDRESS
PLA
TAY
PLA
TAX
^•INITIALIZE
CLC ;CLEAR CARRY
LDA LENGTH
BEQ EXIT ;EXIT IF LENGTH OF OPERAND IS 0
LDA NBITS
BEQ EXIT ;EXIT IF NUMBER OF BITS TO SHIFT IS 0
; WITH CARRY CLEAR
EXIT:
RTS
;DATA SECTION
NBITS: .BLOCK 1 ;NUMBER OF BITS TO SHIFT
LENGTH: .BLOCK 1 ;LENGTH OF OPERAND
SAMPLE EXECUTION:
SC0708:
LDA AYADR+1 ;PUSH STARTING ADDRESS OF OPERAND
PHA
LDA AYADR
PHA
JMP SC0708
;DATA SECTION
SZAY: .EQU 7 ;LENGTH OF OPERAND
SHIFTS: .BYTE 4 ;NUMBER OF SHIFTS
AYADR: .WORD AY ;STARTING ADDRESS OF OPERAND
AY: .BYTE 21H,43H,65H,87H,0A9H,0CBH,0EDH
.END ;PROGRAM
Multiple-Precision Rotate Right (MPRR) 71
Registers used: All anywhere in RAM are temporary storage for the
number of shifts (one byte at address NBITS)
Execution Time: NUMBER OF SHIFTS ♦ (21
and the length of the operand in bytes (one byte
+ 18 * LENGTH OF OPERAND IN BYTES)
at address LENGTH). The two bytes on page 0
+ 85 cycles.
hold a pointer to the operand (starting at address
If for example, NUMBER OF SHIFTS = 6 and PTR, 00D016 in the listing).
LENGTH OF OPERAND IN BYTES = 4 (i.e. a
6-bit shift of a 4-byte operand), the execution Special Cases:
time is 1. If the length of the operand is zero, the
6 * (21 + 18 * 4) + 85 = 6 * (93) + 85 program exits immediately with the operand
+ 643 cycles. unchanged and the Carry flag cleared.
2. If the number of shifts is zero, the pro
Program Size: 63 bytes
gram exits immediately with the operand
Data Memory Required: Two bytes anywhere in unchanged and the Carry flag cleared.
RAM plus two bytes on page 0. The two bytes
337
338 BIT MANIPULATIONS AND SHIFTS
Examples
1. Data: Length of operand (in bytes) = 08 2. Data: Length of operand (in bytes) = 04
Operand = 85A4C719FE06741E16 Operand = 3F6A42D316
Number of shifts = 04 Number of shifts = 03
Result: Shifted operand = E85A4C719F30674116. Result: Shifted operand = 67ED485A16. This is
This is the original operand rotated right the original operand rotated right 3 bits;
four bits: the four most significant bits the three most significant bits (Oil) are
are equivalent to the original four equivalent to the original three least
least significant bits. significant bits.
Carry = 1, since the last bit shifted from Carry = 0, since the last bit shifted
the rightmost bit position was 1. from the rightmost bit position was 0.
;EQUATES
PTR: .EQU 0D0H ;PAGE ZERO FOR POINTER TO OPERAND
71 (MPRR) MULTIPLE-PRECISION ROTATE RIGHT 339
MPRR:
;SAVE RETURN ADDRESS
PLA
TAY
PLA
TAX
PLA
STA LENGTH
^•INITIALIZE
CLC ;CLEAR CARRY
LDA LENGTH
BEQ EXIT ;EXIT IF LENGTH OF THE OPERAND IS 0
LDA NBITS
BEQ EXIT ;EXIT IF NUMBER OF BITS TO SHIFT IS 0
; WITH CARRY CLEAR
EXIT:
RTS
;DATA SECTION
NBITS: .BLOCK 1 ;NUMBER OF BITS TO SHIFT
LENGTH: .BLOCK 1 ;LENGTH OF OPERAND
SAMPLE EXECUTION:
SC0709:
LDA AYADR+1 ;PUSH STARTING ADDRESS OF OPERAND
PHA
LDA AYADR
PHA
;DATA SECTION
SZAY: .EQU 7 ;LENGTH OF OPERAND IN BYTES
SHIFTS: .BYTE 4 ;NUMBER OF SHIFTS
AYADR: .WORD AY ;STARTING ADDRESS OF OPERAND
21H,4 3H,65Hf87H,0A9H,0CBH,0EDH
AY: .BYTE
.END ;PROGRAM
Multiple-Precision Rotate Left (MPRL) 7J
Registers Used: All anywhere in RAM are temporary storage for the
number of shifts (one byte at address NBITS)
Execution Time: NUMBER OF SHIFTS * (27 +
and the length of the operand in bytes (one byte
20 * LENGTH OF OPERAND IN BYTES) + 73
at address LENGTH). The two bytes on page 0
cycles.
hold a pointer to the operand (starting at address
If, for example, NUMBER OF SHIFTS = 4 PTR, 00D016 in the listing).
and LENGTH OF OPERAND IN BYTES = 8
(i.e., a 4-bit shift of an 8-byte operand), the Special Cases:
execution time is 1. If the length of the operand is zero, the
4 * (27 + 20 ♦ 8) + 73 = 4 * (187) + 73 - program exits immediately with the operand
821 cycles. unchanged and the Carry flag cleared.
2. If the number of shifts is zero, the program
Program Size: 60 bytes
exits immediately with the operand unchanged
Data Memory Required: Two bytes anywhere in and the Carry flag cleared.
RAM plus two bytes on page 0. The two bytes
341
342 BIT MANIPULATIONS AND SHIFTS
Examples
1. Data: Length of operand (in bytes) = 08 2. Data: Length of operand (in bytes)~= 04
Operand = 85A4C719FE06741E16 Operand = 3F6A42D316
Number of shifts = 04 Number of shifts = 03
; '
; Purpose: Rotate left a multi-byte operand N bits ;
;
; Entry: TOP OF STACK ;
• Low byte of return address, ;
? High byte of return address, ;
. Number of bits to shift, ;
• Length of the operand in bytes, ;
. Low byte of address of the operand, ;
High byte of address of the operand
MPRL:
;SAVE RETURN ADDRESS
PLA
TAY
PLA
TAX
PLA
STA LENGTH
;INITIALIZE
CLC ;CLEAR CARRY
LDA LENGTH
BEQ EXIT ;EXIT IF THE LENGTH OF THE OPERAND IS 0
LDA NBITS
BEQ EXIT ;EXIT IF NUMBER OF BITS TO SHIFT IS 0
; WITH CARRY CLEAR
EXIT:
RTS
;DATA SECTION
NBITS: .BLOCK 1 ;NUMBER OF BITS TO SHIFT
LENGTH: .BLOCK 1 ;LENGTH OF OPERAND
SAMPLE EXECUTION:
SC0710:
LDA AYADR+1 ;PUSH STARTING ADDRESS OF OPERAND
PHA
LDA AYADR
PHA
;DATA SECTION
SZAY: .EQU 7 ;LENGTH OF OPERAND IN BYTES
SHIFTS: BYTE 4 ;NUMBER OF SHIFTS
AYADR: .WORD AY ;ADDRESS OF OPERAND
AY: .BYTE 21H,43H,65H,87H,0A9H,OCBH,OEDH
.END ;PROGRAM
String Compare (STRCMP) 8A
345
346 STRING MANIPULATIONS
Examples
1. Data: String 1 = 05TRINT' (05 is the length of 3. Data: String 1 = 05TRINT (05 is the length of
the string) the string)
String 2 = 03'END' (03 is the length of String 2 - 06'SYSTEM' (06 is the length
the string) of the string)
Result: Zero flag = 0 (strings are not identical) Result: Zero flag = 0 (strings are not identical)
Carry flag = 1 (string 2 is not larger than Carry flag = 0 (string 2 is larger than
string 1) string 1)
2. Data: String 1 = 05TRINT (05 is the length of We are assuming here that the strings con
the string) sist of ASCII characters. Note that the byte
String 2 = 02TR' (02 is the length of the preceding the actual characters contains a
string)
hexadecimal number (the length of the
Result: Zero flag = 0 (strings are not identical)
Carry flag = 1 (string 2 is not larger than
string), not a character. We have represented
string 1) this byte as two hexadecimal digits in front of
the string; the string itself is surrounded by
The longer string (string 1) is considered single quotation marks.
to be larger. If you want to determine Note also that this particular routine treats
whether string 2 is an abbreviation of string spaces like any other characters. If for exam
1, you could use Subroutine 8C (FIND THE ple, the strings are ASCII, the routine will
POSITION OF A SUBSTRING) and deter find that SPRINGMAID is larger than
mine whether string 2 was part of string 1 and SPRING MAID, since an ASCII M (4D16) is
started at the first character. larger than an ASCII space (2016).
;EQUATES
S1ADR .EQU ODOH ;PAGE ZERO POINTER TO STRING 1
S2ADR .EQU 0D2H ;PAGE ZERO POINTER TO STRING 2
STRCMP:
;GET RETURN ADDRESS
PLA
TAY
PLA
TAX
BEGCMP:
TAX ;X IS THE LENGTH OF THE SHORTER STRING
BEQ TSTLEN ;BRANCH IF LENGTH IS ZERO
SAMPLE EXECUTION:
SC0801:
LDA SADR1+1 ;PUSH STARTING ADDRESS OF STRING 1
PHA
LDA SADR1
PHA
LDA SADR2+1 ;PUSH STARTING ADDRESS OF STRING 2
PHA
LDA SADR2
PHA
JSR STRCMP ;COMPARE
BRK ;RESULT OF COMPARING "STRING 1" AND "STRING 211
;IS STRING 1 LESS THAN STRING 2 SO
; Z=0,C=0
JMP SC0801 ;LOOP FOR ANOTHER TEST
;
;TEST DATA, CHANGE TO TEST OTHER VALUES
SADRl .WORD SI
SADR2 .WORD S2
51 .BYTE 20Hf"STRING 1
52 .BYTE 2OH /'STRING 2
.END ;PROGRAM
String Concatenation (GONCAT) 8B
349
350 STRING MANIPULATIONS
Examples
1. Data: Maximum length of string 1 = 0E16 = 14l0 2. Data: Maximum length of string 1 = 0E16 = 1410
String 1 = 074JOHNSON' (07 is the String 1 = 07'JOHNSON' (07 is the
length of the string) length of the string)
String 2 = 05\ DON' (05 is the length of String 2 = 09\ RICHARD' (09 is the
the string) length of the string)
Note that we are representing the initial byte (containing the length of the string) as two
hexadecimal digits in both examples.
8B STRING CONCATENATION (CONCAT) 351
;EQUATES
S1ADR .EQU ODOH ;PAGE ZERO POINTER TO STRING 1
S2ADR .EQU 0D2H ;PAGE ZERO POINTER TO STRING 2
CONCAT:
CATLP:
LDY S2IDX
LDA (S2ADR),Y ;GET NEXT BYTE FROM STRING 2
LDY S1IDX
STA (S1ADR),Y ;MOVE IT TO END OF STRING 1
INC S1IDX ;INCREMENT STRING 1 INDEX
INC S2IDX ;INCREMENT STRING 2 INDEX
DEC COUNT ;DECREMENT COUNTER
BNE CATLP ;CONTINUE UNTIL COUNT = 0
EXIT:
LDA SlLEN ;UPDATE LENGTH OF STRING 1
LDY #0
STA (S1ADR),Y
LDA STRGOV ;GET OVERFLOW INDICATOR
ROR A ;CARRY = 1 IF OVERLOW, 0 IF NOT
RTS
;
7 DATA
MAXLEN: •BLOCK 1 ;MAXIMUM LENGTH OF Si
SlLEN: .BLOCK 1 ;LENGTH OF Si
S2LEN: .BLOCK 1 ;LENGTH OF S2
S1IDX: .BLOCK 1 ;RUNNING INDEX INTO Si
S2IDX: .BLOCK 1 ;RUNNING INDEX INTO S2
COUNT: .BLOCK 1 ;CONCATENATION COUNTER
STRGOV: .BLOCK 1 ;STRING OVERFLOW FLAG
SAMPLE EXECUTION:
354 STRING MANIPULATIONS
SC0802:
LDA SADR1+1 ;PUSH ADDRESS OF STRING 1
PHA
LDA SADR1
PHA
LDA SADR2+1 ;PUSH ADDRESS OF STRING 2
PHA
LDA SADR2
PHA
LDA #2OH ;PUSH MAXIMUM LENGTH OF STRING I
PHA
JSR CONCAT ;CONCATENATE
BRK ;RESULT OF CONCATENATING "LASTNAME" AND ", FIRSTNAME"
; IS SI = 13H,"LASTNAME, FIRSTNAME11
JMP SCO802 ;LOOP FOR ANOTHER TEST
;
;TEST DATA, CHANGE FOR OTHER VALUES
SADRl .WORD SI ;STARTING ADDRESS OF STRING 1
SADR2 .WORD S2 ;STARTING ADDRESS OF STRING 2
SI .BYTE 8H ;LENGTH OF SI
.BYTE "LASTNAME " ;32 BYTE MAX LENGTH
S2 .BYTE OBH ;LENGTH OF S2
.BYTE 11, FIRSTNAME n ;32 BYTE MAX LENGTH
• END :PROGRAM
Find the Position of a Substring (POS) 8C
Searches for the first occurrence of a Procedure: The program moves through
substring within a string. Returns the index the string searching for the substring until it
at which the substring starts if it is found and either finds a match or the remaining part of
0 if it is not found. The string and the the string is shorter than the substring and
substring are both a maximum of 255 bytes hence cannot possibly contain it. If the
long and the actual characters are preceded substring does not appear in the string, the
by a byte containing the length. Thus, if the program clears the accumulator; otherwise,
substring is found, its starting index cannot the program places the starting index of the
be less than 1 or more than 255. substring in the accumulator.
Registers Used: All SUBLEN), a running index into the string (one
byte at address SIDX), a running index into the
Execution Time: Data-dependent, but the over
substring (one byte at address SUBIDX), a
head is 135 cycles, each successful match of one
search counter (one byte at address COUNT),
character takes 47 cycles, and each unsuccessful
and an index into the string (one byte at address
match of one character takes 50 cycles. The worst
INDEX). The four bytes on page 0 hold pointers
case occurs when the string and substring always
to the substring (two bytes starting at address
match except for the last character in the
substring, such as
SUBSTG, 00D016 in the listing) and to the string
(two bytes starting at address STRING, 00D216
String = 'AAAAAAAAB'
in the listing).
Substring = 4AAB'
Special Cases:
The execution time in that case is 1. If either the string or the substring has a
(STRING LENGTH - SUBSTRING length of zero, the program exits with zero in the
LENGTH + 1) ♦ (47 • (SUBSTRING accumulator, indicating that it did not find the
LENGTH - 1) + 50) + 135 substring.
If, for example, STRING LENGTH = 9 and 2. If the substring is longer than the string,
SUBSTRING LENGTH = 3, the execution time the program exits with zero in the accumulator,
is indicating that it did not find the substring.
(9 - 3 + 1) • (47 • (3 - 1) + 50) + 135 3. If the program returns an index of 1, the
= 7 ♦ 144 + 135 = 1008 + 135 = 1143 substring may be regarded as an abbreviation of
cycles. the string. That is, the substring occurs in the
string, starting at the first character. A typical
Program Size: 124 bytes
example would be a string PRINT and a substring
Data Memory Required: Six bytes anywhere in
RAM plus four bytes on page 0. The six bytes 4. If the substring occurs more than once in
anywhere in RAM are temporary storage for the the string, the program will return only the index
length of the string (one byte at address SLEN), to the first occurrence (the occurrence with the
the length of the substring (one byte at address lowest starting index).
355
356 STRING MANIPULATIONS
Examples
1. Data: String ID4 ENTER SPEED IN MILES 3. Data: String = lO'LET Yl = XI + R7' (10l6
PER HOUR' (1D1(; - 2910 is the = 1610 is the length of the string)
length of the string). Substring = 02lR4' (02 is the length of
Substring = 05'MILES' (05 is the length the substring)
of the substring) Result: Accumulator contains 00, since the
substring WR4' does not appear in the
Result: Accumulator contains 1016 (1610), the
string LET Yl = XI + R7.
index at which the substring 'MILES'
starts. 4. Data: String =074RESTORE' (07 is the length
of the string)
2 Data: String = lB'SALES FIGURES FOR
JUNE 1981'(1B16 = 27IO is the
Substring = 03'RES' (03 is the length of
the substring)
length of the string)
Substring = 044 JUNE' (04 is the length of Result: Accumulator contains 01, the index at
the substring) which the substring "RES" starts. An
index of 01 indicates that the substring
Result: Accumulator contains 1316 (1910), the
could be an abbreviation of the string;
index at which the substring4 JUNE'
such abbreviations are, for example, often
starts.
used in interactive programs (such as
BASIC interpreters) to save on typing and
storage.
8C FIND THE POSITION OF A SUBSTRING (POS) 357
;EQUATES
358 STRING MANIPULATIONS
POS:
;GET RETURN ADDRESS
PLA
TAY ;SAVE LOW BYTE
PLA
TAX ;SAVE HIGH BYTE
LDA INDEX
STA SIDX ;START STRING INDEX AT INDEX
LDA #1
STA SUBIDX ;START SUBSTRING INDEX AT 1
FOUND:
LDA INDEX ;SUBSTRING FOUND, A = STARTING INDEX
JMP EXIT
NOTFND:
LDA #0 ;SUBSTRING NOT FOUND, A = 0
EXIT
RTS
; DATA
SLEN: .BLOCK 1 ;LENGTH OF STRING
SUBLEN: .BLOCK 1 ;LENGTH OF SUBSTRING
SIDX: .BLOCK 1 ;RUNNING INDEX INTO STRING
SUBIDX: .BLOCK 1 ;RUNNING INDEX INTO SUBSTRING
COUNT: .BLOCK 1 ;SEARCH COUNTER
INDEX: .BLOCK 1 ;CURRENT INDEX INTO STRING
SAMPLE EXECUTION:
SC0803:
LDA SADR+1 ;PUSH ADDRESS OF THE STRING
360 STRING MANIPULATIONS
PHA
LDA SADR
PHA
LDA SUBADR+1 ;PUSH ADDRESS OF THE SUBSTRING
PHA
LDA SUBADR
PHA
JSR POS ;FIND POSITION OF SUBSTRING
BRK ;RESULT OF SEARCHING "AAAAAAAAAB1* FOR "AAB" IS
; REGISTER A=8
JMP SC0803 ;LOOP FOR ANOTHER TEST
• END ;PROGRAM
Copy a Substring from a String (COPY) 8D
Copies a substring from a string, given a Procedure: The program exits immediately
starting index and the number of bytes to if the number of bytes to copy, the maximum
copy. The strings are a maximum of 255 length of the substring, or the starting index
bytes long and the actual characters are pre is zero. It also exits immediately if the start
ceded by a byte containing the length. If the ing index exceeds the length of the string. If
starting index of the substring is zero (i.e., none of these conditions holds, the program
the substring would start in the length byte) checks if the number of bytes to copy exceeds
or is beyond the end of the string, the either the maximum length of the substring
substring is given a length of zero and the or the number of characters available in the
Carry flag is set to 1. If the substring would string. If either one is exceeded, the program
exceed its maximum length or would extend reduces the number of bytes to copy
beyond the end of the string, then only the appropriately. It then copies the proper num
maximum number or the available number ber of bytes from the string to the substring.
of characters (up to the end of the string) are The program clears the Carry flag if the
placed in the substring, and the Carry flag is substring can be formed as specified and sets
set to 1. If the substring can be formed as the Carry flag if it cannot.
specified, the Carry flag is cleared.
361
362 STRING MANIPULATIONS
Examples
1. Data: String = 10'LET Yl = R7 + X4' 3. Data: String = 16*9414 HEGENBERGER
(1016 = 161O is the length of the string) DRIVE' (1616 = 2210 is the length
Maximum length of substring = 2 of the string)
Number of bytes to copy = 2 Maximum length of substring = 1016
;EQUATES
DSTRG .EQU 0D0H ;PAGE ZERO POINTER TO DESTINATION STRING
SSTRG .EQU 0D2H ;PAGE ZERO POINTER TO SOURCE STRING
364 STRING MANIPULATIONS
COPY:
;GET RETURN ADDRESS
PLA
TAY ;SAVE LOW BYTE
PLA
TAX ;SAVE HIGH BYTE
LDA INDEX
BEQ EREXIT ;ERROR EXIT IF STARTING INDEX IS ZERO
;CHECK THAT WE DO NOT COPY BEYOND THE END OF THE SOURCE STRING
;IF INDEX + COUNT - 1 > LENGTH(SSTRG) THEN
; COUNT := LENGTH(SSTRG) - INDEX + 1;
LDA INDEX
CLC
ADC COUNT
BCS RECALC ;BRANCH IF INDEX + COUNT > 255
TAX
DEX
CPX SLEN
BCC CNT1OK ;BRANCH IF INDEX + COUNT - 1 < LENGTH(SSTRG)
BEQ CNT1OK ;BRANCH IF EQUAL
;THE CALLER ASKED FOR TOO MANY CHARACTERS JUST RETURN EVERYTHING
; BETWEEN INDEX AND THE END OF THE SOURCE STRING.
; SO SET COUNT := LENGTH(SSTRG) - INDEX + 1;
RECALC:
LDA SLEN ;RECALCULATE COUNT
SEC
SBC INDEX
STA COUNT
INC COUNT ;COUNT := LENGTH(SSTRG) - INDEX + 1
LDA #0FFH
STA CPYERR ;INDICATE A TRUNCATION OF THE COUNT
;CHECK IF THE COUNT IS LESS THAN OR EQUAL TO THE MAXIMUM LENGTH OF THE
; DESTINATION STRING. IF NOT, THEN SET COUNT TO THE MAXIMUM LENGTH
; IF COUNT > MAXLEN THEN
; COUNT := MAXLEN
CNT1OK:
LDA COUNT ;IS COUNT > MAXIMUM SUBSTRING LENGTH ?
CMP MAXLEN
BCC CNT2OK ;BRANCH IF COUNT < MAX LENGTH
BEQ CNT2OK ;BRANCH IF COUNT = MAX LENGTH
LDA MAXLEN
STA COUNT ;ELSE COUNT MAXLEN
LDA #0FFH
STA CPYERR ;INDICATE DESTINATION STRING OVERFLOW
;EVERYTHING IS SE
;GOOD EXIT
OKEXIT:
CLC
BCC EXIT
;ERROR EXIT
EREXIT:
SEC
;DATA SECTION
SLEN: .BLOCK I ;LENGTH OF SOURCE STRING
DLEN-: .BLOCK I ;LENGTH OF DESTINATION STRING
MAXLEN: .BLOCK 1 ;MAXIMUM LENGTH OF DESTINATION STRING
COUNT: .BLOCK 1 ;SEARCH COUNTER
INDEX: .BLOCK 1 ;CURRENT INDEX INTO STRING
CPYERR: .BLOCK 1 ;COPY ERROR FLAG
SAMPLE EXECUTION:
SC0804:
LDA SADR+I ;PUSH ADDRESS OF SOURCE STRING
PHA
LDA SADR
PHA
LDA IDX ;PUSH STARTING INDEX FOR COPYING
PHA
LDA CNT ;PUSH NUMBER OF CHARACTERS TO COPY
PHA
LDA DADR+1 ;PUSH ADDRESS OF DESTINATION STRING
PHA
LDA DADR
PHA
LDA MXLEN ;PUSH MAXIMUM LENGTH OF DESTINATION STRING
PHA
JSR COPY ;COPY
8D COPY A SUBSTRING FROM A STRING (COPY) 367
;
;DATA SECTION
IDX .BYTE 4 ;STARTING INDEX FOR COPYING
CNT .BYTE 3 ;NUMBER OF CHARACTERS TO COPY
MXLEN .BYTE 2 OH ;MAXIMUM LENGTH OF DESTINATION STRING
SADR .WORD SSTG
DADR .WORD DSTG
SSTG .BYTE OAH ;LENGTH OF STRING
.B^TE "12.345E+10 11 ;32 BYTE MAX LENGTH
DSTG .BYTE 0 ;LENGTH OF SUBSTRING
n
.BYTE n ;32 BYTE MAX LENGTH
.END ;PROGRAM
Delete a Substring from a String (DELETE) 8E
Deletes a substring from a string, given a if the starting index or the number of bytes to
starting index and a length. The string is a delete is zero. It also exits if the starting index
maximum of 255 bytes long and the actual is beyond the length of the string. If none of
characters are preceded by a byte containing these conditions holds, the program checks
the length. The Carry flag is cleared if the to see if the string extends beyond the area to
deletion can be performed as specified. The be deleted. If it does not, the program simply
Carry flag is set if the starting index is zero or truncates the string by setting the new length
beyond the length of the string; the string is to the starting index minus 1. If it does, the
left unchanged in either case. If the deletion program compacts the resulting string by
extends beyond the end of the string, the moving the bytes above the deleted area
Carry flag is set (to 1) and only the characters down. The program then determines the new
from the starting index to the end of the string's length and exits with the Carry
string are deleted. cleared if the specified number of bytes were
Procedure: The program exits immediately deleted and set to 1 if any errors occurred.
368
8E DELETE A SUBSTRING FROM A STRING (DELETE) 369
Examples
1. Data: String = lE'SALES FOR MARCH AND 2. Data: String = 28THE PRICE IS $3.00 ($2.00
APRIL 1980* (1E16 = 3010 is the BEFORE JUNE 1)' (2816 = 4010 is the
length of the string) length of the string).
Number of bytes to delete = 0A16 = 10i0 Number of bytes to delete = 3016 = 4810
Starting index to delete from = 1016 = Starting index to delete from = 1316
16io = 1910
Result: String = 14WSALES FOR MARCH 1980' Result: String = 12'THE PRICE IS $3.00' (1216
(1416 = 2010 is the length of the string = 1810 is the length of the string with all
with ten bytes deleted starting with the remaining bytes deleted).
16th character — the deleted material is Carry = 1, since there were not as many
'AND APRIL'). bytes left in the string as were supposed
Carry = 0, since no problems occurred in to be deleted.
the deletion.
?
; A string is a maximum of 255 bytes long plus ;
; a length byte which precedes it. ;
;EQUATES
STRG .EQU 0D0H ; PAGE ZERO POIN
DELETE:
;GET RETURN ADDRESS
PLA
TAY ;SAVE LOW BYTE
PLA
TAX ;SAVE HIGH BYTE
LDA INDEX
BEQ EREXIT ;ERROR EXIT IF STARTING INDEX = 0
MVLP:
LDY SIDX
LDA (STRG),Y ;GET NEXT CHARACTER
LDY INDEX
STA (STRG),Y ;MOVE IT DOWN
INC INDEX ;INCREMENT DESTINATION INDEX
INC SIDX ;INCREMENT SOURCE INDEX
DEX ;DECREMENT COUNTER
BNE MVLP ;CONTINUE UNTIL COUNTER = 0
LDX INDEX
DEX ;STRING LENGTH = FINAL DESTINATION INDEX - 1
STX SLEN
;GOOD EXIT
OKEXIT:
CLC
BCC EXIT
;ERROR EXIT
EREXIT:
SEC
EXIT:
LDA SLEN
LDY #0
STA (STRG),Y jSET LENGTH OF STRING
RTS
; DATA
SLEN: .BLOCK 1 ;LENGTH OF SOURCE STRING
COUNT: .BLOCK 1 ;SEARCH COUNTER
INDEX: .BLOCK 1 ;CURRENT INDEX INTO STRING
SIDX: .BLOCK 1 ;SOURCE INDEX DURING MOVE
DELERR: .BLOCK 1 ;DELETE ERROR FLAG
SAMPLE EXECUTION:
SC0805:
LDA SADR+1 ;PUSH STRING ADDRESS
PHA
LDA SADR
PHA
LDA IDX ;PUSH STARTING INDEX FOR DELETION
8E DELETE A SUBSTRING FROM A STRING (DELETE) 373
PHA
LDA CNT ;PUSH NUMBER OF CHARACTERS TO DELETE
PHA
JSR DELETE ;DELETE
BRK ;RESULT OF DELETING 4 CHARACTERS STARTING AT INDEX 1
; FROM nJOE HANDOVER" IS "HANDOVER"
JMP SCO805 ;LOOP FOR ANOTHER TEST
;DATA SECTION
IDX .BYTE 1 ;INDEX TO START OF DELETION
CNT .BYTE 4 ;NUMBER OF CHARACTERS TO DELETE
SADR • WORD SSTG
SSTG .BYTE 12 ;LENGTH OF STRING
.BYTE "JOE HANDOVER"
• END ;PROGRAM
Insert a Substring into a String (INSERT) 8F
Inserts a substring into a string, given a than the maximum. If it would, the program
starting index. The string and substring are truncates the substring. The program then
both a maximum of 255 bytes long and the checks to see if the starting index is within
actual characters are preceded by a byte con the string. If it is not, the program simply
taining the length. The Carry flag is cleared if concatenates the substring by moving it to
the insertion can be accomplished with no the memory locations immediately after the
problems. The Carry flag is set if the starting end of the string. If the starting index is
index is zero or beyond the length of the within the string, the program must first open
string. In the second case, the substring is a space for the insertion by moving the
concatenated to the end of the string. The remaining characters up in memory. This
Carry flag is also set if the string with the move must start at the highest address to
insertion would exceed a specified maximum avoid writing over any data. Finally, the pro
length; in that case, the program inserts only gram can move the substring into the open
enough of the substring to give the string its area. The program then determines the new
maximum length. string length and exits with the Carry flag set
Procedure: The program exits immediately appropriately (to 0 if no problems occurred
if the starting index is zero or if the length of and to 1 if the starting index was zero, the
the substring is zero. If neither of these con substring had to be truncated, or the starting
ditions holds, the program checks to see if index was beyond the length of the string).
the insertion would produce a string longer
374
8F INSERT A SUBSTRING INTO A STRING (INSERT) 375
Examples
1. Data: String = OA'JOHN SMITH1 (0A16 = 1010 Data: String = OA'JOHN SMITH' (0A16 = 10I0
is the length of the string) is the length of the string)
Substring = 08'WILLIAM ' (08 is the Substring = 0CROCKEFELLER ' (0C16
length of the substring) = 1210 is the length of the substring)
Maximum length of string = 1416 = 2010 Maximum length of string = 1416 = 2010
Starting index = 06 Starting index = 06
;EQUATES
SUBSTG .EQU 0D0H ;PAGE ZERO POINTER TO SUBSTRING
STRG .EQU 0D2H ;PAGE ZERO POINTER TO STRING
INSERT:
;GET RETURN ADDRESS
PLA
TAY ;SAVE LOW BYTE
PLA
TAX ;SAVE HIGH BYTE
;ASSUME NO ERRORS
LDA #0
STA INSERR ;ASSUME NO ERRORS WILL BE FOUND
;CHECK THAT THE RESULTING STRING AFTER THE INSERTION FITS IN THE
; SOURCE STRING. IF NOT THEN TRUNCATE THE SUBSTRING AND SET THE
; TRUNCATION FLAG.
CHKLEN:
LDA SUBLEN ;GET SUBSTRING LENGTH
CLC
ADC SLEN
BCS TRUNC ;TRUNCATE SUBSTRING IF NEW LENGTH > 255
CMP MAXLEN
BCC IDXLEN ;BRANCH IF NEW LENGTH < MAX LENGTH
BEQ IDXLEN ;BRANCH IF NEW LENGTH = MAX LENGTH
OPNLP:
LDY SIDX
LDA (STRG),Y ;GET NEXT CHARACTER
LDY DIDX
STA (STRG),Y ;MOVE IT UP IN MEMORY
DEC SIDX ;DECREMENT SOURCE INDEX
DEC DIDX ;DECREMENT DESTINATION INDEX
DEX ;DECREMENT COUNTER
BNE OPNLP ;CONTINUE UNTIL COUNTER = 0
MVELP:
LDY SIDX
LDA (SUBSTG),Y ;GET NEXT CHARACTER
LDY INDEX
STA (STRG),Y ;STORE CHARACTER
INC SIDX ;INCREMENT SUBSTRING INDEX
INC INDEX ;INCREMENT STRING INDEX
DEX ;DECREMENT COUNT
BNE MVELP ;CONTINUE UNTIL COUNTER = 0
LDA INSERR ;GET ERROR FLAG
380 STRING MANIPULATIONS
OKEXIT:
CLC ;NO ERROR
BCC EXIT
EREXIT:
SEC ;ERROR EXIT
EXIT:
LDA SLEN
LDY #0
STA (STRG),Y ;SET NEW LENGTH OF STRING
RTS
;DATA SECTION
SLEN: .BLOCK 1 ;LENGTH OF STRING
SUBLEN: .BLOCK 1 ;LENGTH OF SUBSTRING
MAXLEN: .BLOCK 1 ;MAXIMUM LENGTH OF STRING
INDEX: .BLOCK 1 ;CURRENT INDEX INTO STRING
SIDX: .BLOCK 1 ;A RUNNING INDEX
DIDX: .BLOCK 1 ;A RUNNING INDEX
INSERR: .BLOCK 1 ;FLAG USED TO INDICATE IF AN ERROR
SAMPLE EXECUTION:
SC0806:
LDA SADR+1 ;PUSH ADDRESS OF SOURCE STRING
PHA
LDA SADR
PHA
LDA IDX ;PUSH STARTING INDEX FOR INSERTION
PHA
LDA MXLEN ;PUSH MAXIMUM LENGTH OF SOURCE STRING
PHA
LDA SUBADR+1 ;PUSH ADDRESS OF THE SUBSTRING
PHA
LDA SUBADR
PHA
JSR INSERT ;INSERT
BRK ;RESULT OF INSERTING "-" INTO "123456" AT
; INDEX 1 IS "-123456"
JMP SC0806 ;LOOP FOR ANOTHER TEST
;DATA SECTION
IDX ..BYTE 1 ;INDEX TO START INSERTION
MXLEN .BYTE 20H ;MAXIMUM LENGTH OF DESTINATION
SADR .WORD STG ;STARTING ADDRESS OF STRING
SUBADR .WORD SSTG ;STARTING ADDRESS OF SUBSTRING
STG .BYTE 06H ;LENGTH OF STRING
8F INSERT A SUBSTRING INTO A STRING (INSERT) 381
.END ;PROGRAM
8-Bit Array Summation (ASUM8) 9A
sum, starting with the element at the highest Program Size: 30 bytes
address. Whenever an addition produces a Data Memory Required: Two bytes on page 0 to
hold a pointer to the array (starting at address
carry, the program increments the more sig
ARYADR, 00D016 in the listing).
nificant byte of the sum.
Special Case: An array size of zero causes an
immediate exit with the sum equal to zero.
(A) = More significant byte of starting (A) = More significant byte of sum
address of array (Y) = Less significant byte of sum
(Y) = Less significant byte of starting
address of array
(X) = Size of array in bytes
Example
Data: Size of array (in bytes) = (X) = 08 Result: Sum = 03D716 = 98310
Array elements (A) = more significant byte of sum
F7,* = 247 10 = 0316
(Y) = less significant byte of sum = D7I6
23,6 = 35,0
31,6 = 4910
70,6 = 112,0
5A16 = 9O,o
16,6 = 22,o
CB,6 = 203,0
El,6 = 225,0
382
9A 8-BIT ARRAY SUMMATION (ASUM8) 383
EQUATES SECTION
ARYADR: .EQU ODOH ;PAGE ZERO POINTER TO ARRAY
ASUM8:
;INITIALIZATION
LDA #0 ;INITIALIZE SUM TO 0
TAX
;SUMMATION LOOP
SUMLP:
CLC
ADC (ARYADR),Y ;ADD NEXT BYTE TO LSB OF SUM
BCC DECCNT
INX ;INCREMENT MSB OF SUM IF A CARRY OCCURS
384 ARRAY OPERATIONS
DECCNT:
DEY ;DECREMENT COUNT
BNE SUMLP ;CONTINUE UNTIL REGISTER Y EQUALS 0
EXIT:
TAY ;REGISTER Y = LOW BYTEOF SUM
TXA ;REGISTER A = HIGH BYTE OF SUM
RTS
SAMPLE EXECUTION
;
SC09U1:
LDY BUFADR ;Y IS LOW BYTE OF BUFFER ADDRESS
LDA BUFADR+1 ;A IS HIGH BYTE OF BUFFER ADDRESS
LDX BUFSZ ;X IS SIZE OF BUFFER
JSR ASUM8
BRK ;SUM OF THE INITIAL TEST DATA IS 07F8 HEX,
; REGISTER A = 07, REGISTER Y = F8H
JMP SC0901
.END ;PROGRAM
16-Bit Array Summation (ASUM16) 9B
accumulator in three bytes of memory and Data Memory Required: Three bytes anywhere
then adds the elements to the memory in RAM plus two bytes on page 0. The three bytes
anywhere in RAM hold the memory accumulator
accumulator, starting at the lowest address. (starting at address SUM); the two bytes on page
The most significant byte of the memory 0 hold a pointer to the array (starting at address
accumulator is incremented each time the ARYADR, 00D016 in the listing).
addition of the more significant byte of an Special Case: An array size of 0 causes an
immediate exit with the sum equal to zero.
element and the middle byte of the sum pro
duces a carry. If the array occupies more than
one page of memory, the program must array pointer before proceeding to the second
increment the more significant byte of the page.
(A) = More significant byte of starting (X) = Most significant byte of sum
address of array (A) = Middle byte of sum
(Y) = Less significant byte of starting (Y) = Least significant byte of sum
address of array
(X) = Size of array in 16-bit words
Example
Data: Size of array (in 16-bit words) (X) = 08 Result: Sum = 03DBAl16 = 252,83310
Array elements (X) = most significant byte of sum = 0316
F7A116 = 63,393,0 (A) = middle byte of sum = DB16
239B,6 = 9,11510
(Y) = least significant byte of sum = Al16
31D5,6 = 12,75710
70F2l6 = 28,914,0
5A36I6 = 23,094,0
166CI6 = 5,74O,o
CBF516 - 52,21310
E10716 = 57,60710
385
386 ARRAY OPERATIONS
;EQUATES SECTION
ARYADR: .EQU 0D0H ;PAGE ZERO POINTER TO ARRAY
ASUM16:
;SUMMATION LOOP
SUMLP:
LDA SUM
CLC
ADC (ARYADR),Y ;ADD LOW BYTE OF ELEMENT TO SUM
STA SUM
9B 16-BIT ARRAY SUMMATION (ASUM16) 387
LDA SUM+1
INY ;INCREMENT INDEX TO HIGH BYTE OF ELEMENT
ADC (ARYADR),Y ;ADD HIGH BYTE WITH CARRY TO SUM
STA SUM+1 ;STORE IN MIDDLE BYTE OF SUM
BCC NXTELM
INC SUM+2 ;INCREMENT HIGH BYTE OF SUM IF A CARRY
NXTELM:
INY ;INCREMENT INDEX TO NEXT ARRAY ELEMENT
BNE DECCNT
INC ARYADR+1 ;MOVE POINTER TO SECOND PAGE OF ARRAY
DECCNT:
DEX ;DECREMENT COUNT
BNE SUMLP ;CONTINUE UNTIL REGISTER X EQUALS 0
EXIT:
LDY SUM ;Y=LOW BYTE
LDA SUM+1 ;A=MIDDLE BYTE
LDX SUM+2 ;X=HIGH BYTE
RTS
DATA SECTION
SUM: .BLOCK ;TEMPORARY 24 BIT ACCUMULATOR IN MEMORY
SAMPLE EXECUTION
SC0902:
LDY BUFADR ;A,Y = STARTING ADDRES OF BUFFER
LDA BUFADR+1
LDX BUFSZ ;X = BUFFER SIZE IN WORDS
JSR ASUM16 ;RESULT OF THE INITIAL TEST DATA IS 12570
BRK ; REGISTER X = 0, REGISTER A = 31H,
; REGISTER Y = 1AH
JMP SC0902 ;LOOP FOR MORE TESTING
.WORD 1313
.WORD 1414
.WORD 1515 ;SUM = 12570 = 311AH
.END ;PROGRAM
Find Maximum Byte-Length Element (MAXELM) 9C
Finds the maximum element in an array that the last byte of the array is the largest and
of unsigned byte-length elements. The size of then proceeds backward through the array,
the array is specified and is a maximum of comparing the supposedly largest element to
255 bytes. the current element and retaining the larger
Procedure: The program exits immediately value and its index. Finally, the program
(setting Carry to 1) if the array size is zero. If clears the Carry to indicate a valid result.
the size is non-zero, the program assumes
Example
Data: Size of array (in bytes) (X) = 08 Result: The largest unsigned element is element
Array elements #2(D216 = 21010)
3516 - 53IO 44 16 ' 68 10 (A) = largest element (D216)
A6,6 = 166,0 591(; = 89 10 (Y) = index to largest element (02)
D216 = 210,0 7A 16 : 122,0 Carry flag = 0, indicating that array size is
1B,6 = 2710 CFI6 = 207,0 non-zero and the result is valid
389
390 ARRAY OPERATIONS
;EQUATES
ARYADR: .EQU ODOH ;PAGE ZERO FOR ARRAY POINTER
MAXELM:
;STORE STARTING ARRAY ADDRESS
STA ARYADR+1
STY ARYADR
TXA
BEQ EREXIT ;ERROR EXIT IF SIZE IS ZERO
TAY ;REGISTER Y = SIZE AND INDEX
LDA (ARYADR),Y ;GET LAST BYTE OF ARRAY
STY INDEX ;SAVE ITS INDEX
9C FIND MAXIMUM BYTE-LENGTH ELEMENT (MAXELM) 391
DEY
BEQ OKEXIT ;EXIT IF ONLY ONE ELEMENT
;WORK FROM THE END OF THE ARRAY TOWARDS THE BEGINNING COMPARING
; AGAINST THE CURRENT MAXIMUM WHICH IS IN REGISTER A
MAXLP:
CMP (ARYADR),Y
BEQ NEWIDX ;REPLACE INDEX ONLY IF ELEMENT = MAXIMUM
BCS NXTBYT ;BRANCH IF CURRENT MAXIMUM > ARY[Y]
;ELSE ARY[Y] >= CURRENT MAXIMUM SO
LDA (ARYADR),Y ; NEW CURRENT MAXIMUM AND
NEWIDX: STY INDEX ; NEW INDEX
NXTBYT:
DEY ;DECREMENT TO NEXT ELEMENT
BNE MAXLP ;CONTINUE
;EXIT
OKEXIT:
LDY INDEX ;GET INDEX OF THE MAXIMUM ELEMENT
DEY FORMALIZE INDEX TO (O,SIZE-1)
CLC ;NO ERRORS
RTS
EREXIT:
SEC ;ERROR, NO ELEMENTS IN THE ARRAY
RTS
;DATA SECTION
INDEX: .BLOCK 1 ;INDEX OF LARGEST ELEMENT
SAMPLE EXECUTION:
SC0903:
LDA AADR+1 ;A,Y = STARTING ADDRESS OF ARRAY
LDY AADR
LDX #SZARY ;X = SIZE OF ARRAY
JSR MAXELM
BRK ;RESULT FOR THE INITIAL TEST DATA IS
; A = FF HEX (MAXIMUM), Y=08 (INDEX TO MAXIMUM)
JMP SC0903 ;LOOP FOR MORE TESTING
ARY: .BYTE 8
.BYTE 7
.BYTE 6
.BYTE 5
392 ARRAY OPERATIONS
BYTE 4
BYTE 3
BYTE 2
BYTE 1
BYTE OFFH
BYTE OFEH
BYTE OFDH
BYTE OFCH
BYTE OFBH
BYTE OFAH
BYTE 0F9H
BYTE ' 0F8H
END ;PROGRAM
Find Minimum Byte-Length Element (MINELM) 9D
Finds the minimum element in an array assumes that the last byte of the array is the
of unsigned byte-length elements. The size of smallest and then proceeds backward through
the array is specified and is a maximum of the array, comparing the supposedly smallest
255 bytes. element to the current element and retaining
Procedure: The program exits im the smaller value and its index. Finally, the
mediately, setting Carry to 1, if the array size program clears the Carry flag to indicate a
is zero. If the size is non-zero, the program valid result.
Example
Data: Size of array (in bytes) (X) = 08 Result: The smallest unsigned element is element
Array elements #3(1B,6 = 27,O)
3516 = 53,o (A) = smallest element (1B,6)
A6,6 = 16610 ■89I( (Y) = index to smallest element (03)
D2,6 = 210,0 7A 16 " • 122 Carry flag = 0, indicating that array size is
1B,6 = 27,o CF,6 = 207 non-zero and the result is valid.
393
394 ARRAY OPERATIONS
<'
;EQUATES
ARYADR: .EQU ODOH ;PAGE ZERO POINTER TO ARRAY
NINELM:
;STORE STARTING ARRAY ADDRESS
STA ARYADR+1
STY ARYADR
TXA
BEQ EREXIT ;ERROR EXIT IF SIZE IS ZERO
TAY ;REGISTER Y = SIZE AND INDEX
LDA (ARYADR),Y ;GET LAST BYTE OF ARRAY
STY INDEX ;SAVE ITS INDEX
9D FIND MINIMUM BYTE-LENGTH ELEMENT (MINELM) 395
DEY
BEQ OKEXIT ;EXIT IF ONLY ONE ELEMENT
;WORK FROM THE END OF THE ARRAY TOWARDS THE BEGINNING COMPARING
; AGAINST THE CURRENT MINIMUM WHICH IS IN REGISTER A
MINLP:
CMP (ARYADR)fY
BEQ NEWIDX ;REPLACE INDEX IF MINIMUM = ELEMENT
BCC NXTBYT ;BRANCH IF CURRENT MINIMUM < ELEMENT
;ELSE ELEMENT <= CURRENT MINIMUM
LDA (ARYADR),Y ; NEW CURRENT MINIMUM AND
NEWIDX: STY INDEX ; NEW INDEX
NXTBYT:
DEY DECREMENT TO NEXT BYTE
BNE MINLP
;EXIT
OKEXIT:
LDY INDEX ;GET INDEX OF THE MINIMUM ELEMENT
DEY ;NORMALIZE INDEX TO (O,SIZE-1)
CLC ;NO ERRORS
RTS
EREXIT:
SEC ;ERROR, NO ELEMENTS IN THE ARRAY
RTS
;DATA SECTION
INDEX: .BLOCK 1 ;INDEX OF SMALLEST ELEMENT
SAMPLE EXECUTION:
SC0904:
LDA AADR+1 ;A,Y = STARTING ADDRESS OF ARRAY
LDY AADR
LDX #SZARY ;X = SIZE OF ARRAY
JSR MINELM
BRK ,-RESULT FOR THE INITIAL TEST DATA IS
; A = 01H (MINIMUM) , Y=07 (INDEX TO MINIMUM)
JMP SC0904 •LOOP FOR MORE TESTING
BYTE 3
BYTE 2
BYTE 1
BYTE OFFH
BYTE OFEH
BYTE OFDH
BYTE OFCH
BYTE OFBH
BYTE OFAH
BYTE 0F9H
BYTE 0F8H
END ;PROGRAM
Binary Search (BINSCH) 9E
397
398 ARRAY OPERATIONS
Examples
Length of array = 1016 = 1610
Elements of array are 0116,0216,0516,07l6> 0916,0916,
0D16,1016, 2E16> 3716, 5D16, 7E16, Al16, B416, D716, E016
Result: Carry = 0, indicating value found Result: Carry = 1, indicating value not found
(A) == 06, the index of the value in the
array
9E BINARY SEARCH (BINSCH) 399
;EQUATES SECTION
ARYADR: .EQU 0D0H ;PAGE ZERO POINTER TO ARRAY
BINSCH:
;GET RETURN ADDRESS
PLA
TAY
PLA
TAX
;
;CHECK THAT LENGTH IS NOT ZERO
LDX UBND ;GET LENGTH
BEQ NOTFND ;EXIT NOT FOUND IF LENGTH EQUALS ZERO
;SEARCH LOOP
; COMPUTE NEXT INDEX TO BE HALF WAY BETWEEN UPPER BOUND AND
; LOWER BOUND
NXTBYT:
LDA UBND
CLC
ADC LBND ;ADD LOWER AND UPPER BOUNDS
ROR A ;DIVIDE BY 2, TRUNCATING FRACTION
TAY ;REGISTER Y BECOMES INDEX
;IF INDEX IS GREATER THAN UPPER BOUND THEN THE ELEMENT IS NOT HERE
CPY UBND
BEQ TSTLB ;BRANCH IF INDEX EQUALS UPPER BOUND
BCS NOTFND ;BRANCH IF INDEX IS GREATER THAN UPPER BOUND
;IF INDEX IS LESS THAN LOWER BOUND THEN THE ELEMENT IS NOT HERE
TSTLB:
CPY LBND
BCC NOTFND ;BRANCH IF INDEX IS LESS THAN LOWER BOUND
;DATA SECTION
VALUE .BLOCK 1 ;VALUE TO FIND
LBND .BLOCK 1 ;INDEX OF LOWER BOUND
UBND .BLOCK 1 ;INDEX OF UPPER BOUND
SAMPLE EXECUTION
;
SC0905:
;SEARCH FOR A VALUE WHICH IS IN THE ARRAY
LDA BFADR+1
PHA ;PUSH HIGH BYTE OF STARTING ADDRESS
LDA BFADR
PHA ;PUSH LOW BYTE OF STARTING ADDRESS
LDA BFSZ
PHA ;PUSH LENGTH (SIZE OF ARRAY)
LDA #7
PHA ;PUSH VALUE TO FIND
JSR BINSCH ;SEARCH
BRK 7CARRY FLAG SHOULD BE 0 AND REGISTER A = 4
; DATA
SIZE .EQU 01 OH ;SIZE OF BUFFER
BFADR: .WORD BF ;STARTING ADDRESS OF BUFFER
BFSZ: .BYTE SIZE ;SIZE OF BUFFER
BF: ;BUFFER
• BYTE 1
.BYTE 2
.BYTE 4
.BYTE 5
.BYTE 7
.BYTE 9
.BYTE 10
.BYTE 11
.BYTE 23
.BYTE 50
.BYTE 81
.BYTE 123
.BYTE 191
.BYTE 199
.BYTE 250
.BYTE 255
.END ;PROGRAM
Bubble Sort (BUBSRT) 9F
403
404 ARRAY OPERATIONS
Example
;
;EQUATES SECTION
A1ADR: .EQU 0D0H ;ADDRESS OF FIRST ELEMENT
A2ADR: -EQU 0D2H ;ADDRESS OF SECOND ELEMENT
;
BUBSRT:
;GET THE PARAMETERS FROM THE STACK
PLA
TAy ;SAVE LOW BYTE OF RETURN ADDRESS
9F BUBBLE SORT (BUBSRT) 405
PLA
TAX ;SAVE HIGH BYTE OF RETURN ADDRESS
PLA
STA LEN ;SAVE THE LENGTH (SIZE)
PLA
STA A1ADR ;SAVE THE LOW BYTE OF THE ARRAY ADDRESS
CLC
ADC #1
STA A2ADR ;SET LOW BYTE OF A2ADR TO AlADR + 1
PLA
STA A1ADR+1 ;SAVE THE HIGH BYTE OF THE ARRAY ADDRESS
ADC #0
STA A2ADR+1 ;SET HIGH BYTE OF A2ADR
TXA
PHA ;RESTORE HIGH BYTE OF RETURN ADDRESS
TYA
PHA ;RESTORE LOW BYTE OF RETURN ADDRESS
AFTSWP:
INY ;INCREMENT TO NEXT ELEMENT
DEX
BNE INLOOP ;BRANCH NOT DONE WITH INNER LOOP
DONE:
RTS
;DATA SECTION
LEN: .BLOCK 1 ;LENGTH OF THE ARRAY
XCHGFG: .BLOCK 1 ;EXCHANGE FLAG (1=EXCHANGE, 0=NO EXCHANGE)
SAMPLE EXECUTION
;PROGRAM SECTION
SC0906:
;SORT AN ARRAY
LDA BFADR+1
PHA ;PUSH HIGH BYTE OF STARTING ADDRESS
LDA BFADR
PHA ;PUSH LOW BYTE OF STARTING ADDRESS
LDA BFSZ
PHA ;PUSH LENGTH (SIZE OF ARRAY)
JSR BUBSRT ;SORT
BRK ;THE RESULT FOR THE INITIAL TEST DATA IS
; 0,1,2,3, ... ,14,15
JMP SC0906 ;LOOP FOR MORE TESTS
; DATA SECTION
SIZE • EQU 010H ;SIZE OF BUFFER
BFADR: .WORD BF ;STARTING ADDRESS OF BUFFER
BFSZ: .BYTE SIZE ;SIZE OF BUFFER
BF: ;BUFFER
.BYTE 15
.BYTE 14
.BYTE 13
.BYTE 12
.BYTE 11
.BYTE 10
.BYTE 9
.BYTE 8
.BYTE 7
.BYTE 6
.BYTE 5
.BYTE 4
.BYTE 3
.BYTE 2
.BYTE 1
.BYTE 0
.END ;PROGRAM
RAM Test (RAMTST) 9G
all tests can be performed; if it finds an error Data Memory Required: Six bytes anywhere in
RAM plus two bytes on page 0. The six bytes any
it immediately exits, setting the Carry flag
where in RAM hold the address of the first ele
and returning the address in which the error ment (two bytes starting at address ADDR), the
occurred and the value that was being used in length of the tested area (two bytes starting at
address LEN), and the temporary length (two
the test.
bytes starting at address TLEN). The two bytes
Procedure: The program performs the on page 0 hold a pointer to the tested area (start
single value checks (with 00, FF16, AA16, and ing at address TADDR, 00D016 in the listing).
5516) by first filling the memory area and then Special Cases:
comparing each byte with the specified value. 1. An area size of 000016 causes an immediate
exit with no memory tested. The Carry flag is
Filling the entire area first should provide
cleared to indicate no errors.
enough delay between writing and reading to
2. Since the routine changes all bytes in the
detect a failure to retain data (perhaps caused tested area, using it to test an area that includes
by improperly designed refresh circuitry). its own temporary storage will produce unpre
dictable results.
The program then performs the walking bit
Note that Case 1 means you cannot ask this
test, starting with bit 7; here it writes the data routine to test the entire memory, but such a
into memory and immediately attempts to request would be meaningless anyway since it
would require the routine to test its own tempor
read it back for a comparison. In all the tests,
ary storage.
the program handles complete pages first and 3. Attempting to test a ROM area will cause a
then handles the remaining partial page; the return with an error indication as soon as the pro
program can thus use 8-bit counters rather gram attempts to store a value in a ROM location
that is not already there.
than a 16-bit counter. This approach reduces
execution time but increases memory usage
as compared to handling the entire area with
one loop. Note that the program exits the value being used in the test. If all the tests
immediately if it finds an error, setting the can be performed correctly, the program
Carry flag to 1 and returning the location and clears the Carry flag before exiting.
407
408 ARRAY OPERATIONS
1. If an error is found,
Less significant byte of return address
Carry = 1
More significant byte of return address
(A) = More significant byte of address
Less significant byte of size (length) of area containing error
in bytes
(Y) = Less significant byte of address
More significant byte of size (length) of area
containing error
in bytes
(X) = Expected value (value being used
Less significant byte of starting address of in test)
test area
2. If no error is found,
More significant byte of starting address of
test area
Carry = 0
All bytes in test area contain 00.
Example
Data: Starting address = 0380 16 3. Write and read AA,6(101010102)
Length (size) of area = 020016 4. Write and read 55,6(010101012)
Result: Area tested is the 020016 bytes, starting at 5. Walking bit test, starting with bit 7 and moving
addresses 038016. That is, address 038016 right. That is, starting with 8016 (10000002) and moving
through 057F16. The order of the tests is: the 1 bit one position right in each subsequent test of a
1. Write and read 00 single byte.
2. Write and read FF16
;EQUATES SECTION
RAMTST:
;GET THE RETURN ADDRESS
PLA
TAY
PLA
TAX
;FILL MEMORY WITH AA HEX (ALTERNATING l'S AND O'S) AND COMPARE
LDA #0AAH
JSR FILCMP
BCS EXITER ;EXIT IF AN ERROR
;FILL MEMORY WITH 55 HEX (ALTERNATING O'S AND l'S) AND COMPARE
LDA #55H
JSR FILCMP
BCS EXITER ;EXIT IF AN ERROR
WLKLP:
LDA #80H ;SET BIT 7 TO 1, ALL OTHER BITS TO ZERO
WLKLP1:
STA (TADDR),Y ;STORE TEST PATTERN IN MEMORY
CMP (TADDR),Y ;COMPARE VALUE WITH WHAT IS READ BACK
BNE EXITER ;EXIT INDICATING ERROR IF NOT THE SAME
LSR A ;SHIFT TEST PATTERN RIGHT ONE BIT
BNE WLKLP1 ;BRANCH IF NOT DONE WITH BYTE
STA (TADDR),Y ;STORE A ZERO BACK INTO THE LAST BYTE
INY ;INCREMENT TO NEXT BYTE IN PAGE
BNE WLKLP ;BRANCH IF NOT DONE WITH PAGE
INC TADDR+1 .•INCREMENT TO NEXT PAGE
DEX ;DECREMENT PAGE COUNTER
BNE WLKLP ;BRANCH IF NOT DONE WITH ALL OF THE PAGES
WLKPRT:
LOX TLEN ;GET NUMBER OF BYTES IN LAST PAGE
BEQ EXITOK ;EXIT IF NONE
LDY #0 ;INITIALIZE INDEX TO ZERO
WLKLP2:
LDA #80H ;START WITH BIT 7 EQUAL TO 1
WLKLP3:
STA (TADDR),Y ;STORE TEST PATTERN IN MEMORY
CMP (TADDR),Y ;COMPARE VALUE WITH WHAT IS READ BACK
BNE EXITER ;EXIT INDICATING ERROR IF NOT THE SAME
LSR A ;SHIFT TEST PATTERN RIGHT
BNE WLKLP3 ;BRANCH IF NOT DONE
STA (TADDR),Y ;STORE A ZERO BACK INTO THE LAST BYTE
INY ;INCREMENT TO NEXT BYTE
DEX ;DECREMENT BYTE COUNTER
BNE WLKLP2 ;BRANCH IF NOT DONE
EXITOK:
CLC ;RETURN WITH NO ERROR
RTS
EXITER:
JSR ERROR ;RETURN WITH AN ERROR
RTS
************************************
;ROUTINE: FILCMP
;PURPOSE: FILL MEMORY WITH A VALUE AND TEST
; THAT MEMORY CONTAINS THAT VALUE
;ENTRY: REGISTER A = VALUE
; ADDR = STARTING ADDRESS
; LEN = LENGTH
;EXIT: IF NO ERRORS THEN
CARRY FLAG EQUALS 0
; ELSE
CARRY FLAG EQUALS 1
; REGISTER A = HIGH BYTE OF ERROR LOCATION
REGISTER Y = LOW BYTE OF ERROR LOCATION
REGISTER X = EXPECTED VALUE
;REGISTERS USED: ALL
.a***********************************
FILCMP:
JSR ITEMPS ;INITIALIZE TEMPORARIES
FILPRT:
;FILL PARTIAL PAGE
LDX TLEN ;REGISTER Y IS SET TO SIZE OF LAST PAGE
LDY #0
FILLP1:
STA (TADDR),Y
INY
DEX
BNE FILLP1 ;CONTINUE
;COMPARE MEMORY AG
CMPARE:
JSR ITEMPS ;INITIALIZE TEMPORARIES
CMPPRT:
;COMPARE THE LAST
LDX TLEN ;REGISTER Y = SIZE OF PARTIAL PAGE
LDY #0
CMPLP1:
CMP (TADDR),Y ;CAN THE STORED VALUE BE READ BACK ?
BNE CMPER ;NO, EXIT INDICATING ERROR
INY
DEX
BNE CMPLP1 ;CONTINUE
CMPOK:
CLC ;INDICATE NO ERROR
RTS
CMPER:
JSR ERROR
RTS
;ROUTINE: ITEMPS
;PURPOSE: INITIALIZE TEMPORARIES
9G RAM TEST (RAMTST) 413
ITEMPS:
LDY ADDP
STY TADDR
LDY ADDR+1
STY TADDR+1
LDY LEN
STY TLEN
LDY LEN+1
STY TLEN+1
RTS
.••a************************************
;ROUTINE: ERROR
;PURPOSE: SET UP THE REGISTERS FOR AN ERROR EXIT
;ENTRY: REGISTER A IS EXPECTED BYTE
; TADDR IS BASE ADDRESS
REGISTER Y IS INDEX
;EXIT REGISTER X IS SET TO EXPECTED BYTE
; REGISTER A IS SET TO HIGH BYTE OF THE ADDRESS CONTAINING THE ERROR
; REGISTER Y IS SET TO LOW BYTE OF THE ADDRESS CONTAINING THE ERROR
CARRY FLAG IS SET TO 1
;REGISTERS USED: ALL
.•a************************************
ERROR:
TAX ;REGISTER X = EXPECTED BYTE
TYA ;GET INDEX
CLC ;ADDRESS OF ERROR » BASE + INDEX
ADC TADDR
TAY ;REGISTER Y = LOW BYTE OF ERROR LOCATION
LDA TADDR+1
ADC #0 ;REGISTER A = HIGH BYTE OF ERROR LOCATION
SEC ;INDICATE AN ERROR BY SETTING CARRY TO 1
RTS
; DATA SECTION
ADDR: .BLOCK 2 ;ADDRESS OF FIRST ELEMENT
LEN: .BLOCK 2 ;LENGTH
TLEN: .BLOCK 2 ;TEMPORARY LENGTH
SAMPLE EXECUTION
414 ARRAY OPERATIONS
SC0907:
;TEST MEMORY
LDA ADR+1
PHA ;PUSH HIGH BYTE OF STARTING ADDRESS
LDA ADR
PHA ;PUSH LOW BYTE OF STARTING ADDRESS
LDA SZ+1
PHA ;PUSH HIGH BYTE OF LENGTH
LDA SZ
PHA ;PUSH LOW BYTE OF LENGTH
JSR RAMTST ;TEST
BRK ;CARRY FLAG SHOULD BE 0
JMP SC0907 ;LOOP FOR MORE TESTING
• END ;PROGRAM
Jump Table (JTAB) 9H
must be less than or equal to 128. If the index Data Memory Required: Two bytes anywhere in
RAM (starting at address TMP) to hold the
is greater than or equal to LENSUB, the pro
indirect address obtained from the table.
gram returns control immediately with the
Special Case: Entry with (A) greater than or
Carry flag set to 1. equal to LENSUB causes an immediate exit with
Procedure: The program first checks if the Carry flag set to 1.
index is greater than or equal to the size of
the table (LENSUB). If it is, the program of the appropriate subroutine from the table,
returns control with the Carry flag set. If it is stores it in memory, and jumps to it
not, the program obtains the starting address indirectly.
Example
Data: LENSUB (size of subroutine table) = 03.
Table consists of addresses SUBO, SUB1
and SUB2.
Index = (A) = 02
415
416 ARRAY OPERATIONS
JTAB:
CMP tLENSUB
BCS JTABER ?BRANCH IF REGISTER A IS TOO LARGE
ASL A ;MULTIPLY VALUE BY 2 FOR WORD-LENGTH INDEX
TAY
LDA TABLE,Y ;MOVE STARTING ADDRESS TO TEMPORARY STORAGE
STA TMP
LDA TABLE+1,Y
STA TMP+1
JMP (TMP) ;JUMP INDIRECTLY TO SUBROUTINE
JTABER:
SEC ;INDICATE A BAD ROUTINE NUMBER
RTS
LENSUB .EQU 3
TABLE:
.WORD SUBl ;ROUTINE 0
.WORD SUB2 ;ROUTINE 1
.WORD SUB 3 ;ROUTINE 2
SUB 2:
LDA #2
RTS
SUB3:
LDA #3
RTS
;
SAMPLE EXECUTION ;
;
;
;PROGRAM SECTION
SC0908:
LDA #0
JSR JTAB
BRK ;EXECUTE ROUTINE 0, REGISTER A EQUALS 1
LDA #1
JSR JTAB
BRK ;EXECUTE ROUTINE 1, REGISTER A EQUALS 2
LDA #2
JSR JTAB
BRK ;EXECUTE ROUTINE 2, REGISTER A EQUALS 3
LDA #3
JSR JTAB
BRK ;ERROR CARRY FLAG EQUALS 1
JMP SC0908 ;LOOP FOR MORE TESTS
• END ;PROGRAM
Read a Line of Characters from a Terminal
(RDLINE) 10A
which deletes all characters in the buffer. Data Memory Required: Four bytes anywhere in
RAM plus two bytes on page 0. The four bytes
Sends a bell character (07 hex) to the ter
anywhere in RAM hold the buffer index (one
minal if the buffer becomes full. Echoes to byte at address BUFIDX), the buffer length (one
the terminal each character placed in the byte at address BUFLEN), the count for the
backspace routine (one byte at address COUNT),
buffer. Sends a new line sequence (typically
and the index for the backspace routine (one byte
carriage return, line feed) to the terminal at address INDEX). The two bytes on page 0 hold
before exiting. a pointer to the input buffer (starting at address
BUFADR, 00D016 in the listing).
RDLINE assumes the existence of the
Special Cases:
following system-dependent subroutines:
1. Typing Control H (delete one character) or
Control X (delete the entire line) when there is
1. RDCHAR reads a single character from nothing in the buffer has no effect on the buffer
the terminal and places it in the accumulator. and does not cause anything to be sent to the ter
minal.
2. WRCHAR sends the character in the 2. If the program receives an ordinary
accumulator to the terminal. character when the buffer is full, it sends a Bell
character to the terminal (ringing the bell), dis
3. WRNEWL sends a new line sequence cards the received character, and continues its
(typically consisting of carriage return and normal operations.
line feed characters) to the terminal.
These subroutines are assumed to change 2. The entry point for the routine that
the contents of all the user registers. sends a character to the monitor is FDED16.
RDLINE is intended as an example of a This routine requires bit 7 of the character
typical terminal input handler. The specific (in the accumulator) to be set.
control characters and I/O subroutines in a
3. The entry point for the routine that
real system will, of course, be computer-
issues the appropriate new line character (a
dependent. A specific example in the listing carriage return) is FD8E16.
describes an Apple II computer with the
4. An 0816 character moves the cursor left
following features:
one position.
418
10A READ A LINE OF CHARACTERS (RDLINE) 419
Procedure: The program first reads a response to Control H until it empties the
character using the RDCHAR routine and buffer. If the character is not special, the pro
exits if the character is a carriage return. If gram checks to see if the buffer is full. If the
the character is not a carriage return, the pro buffer is full, the program sends a bell
gram checks for the special characters Con character to the terminal and continues. If
trol H and Control X. In response to Control the buffer is not full, the program stores the
H, the program decrements the buffer index character in the buffer, echoes it to the ter
and sends a backspace string (consisting of minal, and adds one to the buffer index.
cursor left, space, cursor left) to the terminal Before exiting, the program sends a new line
if there is anything in the buffer. In response sequence to the terminal using the
to Control X, the program repeats the WRNEWL routine.
(A) = More significant byte of starting (X) = Number of characters in the buffer.
address of buffer
(Y) = Less significant byte of starting
address of buffer
(X) = Length (size) of the buffer in bytes.
Examples
1. Data: Line (from keyboard is 'ENTERcr' The sequence of operations is as follows:
Result: Buffer index = 5 (length of line) Character Initial Final
Buffer contains 'ENTER' Typed Buffer Buffer
'ENTER' echoed to terminal, followed by D empty 'D'
the new line sequence (typically either car
M 'D' 'DM'
riage return, line feed or just carriage
return) control H 'DM' 'D'
Note that the 'cr' (carriage return) N 'D' 'DN'
character does not appear in the buffer. control X 'DN' empty
What has happened is the following: deleted is not the latest one, the operator types control
X to delete the entire line, and then types 'ENTET\
a. The operator types 4D\ lM'
d. The operator recognizes that the second T' is
b. The operator recognizes that 'NT is incorrect
incorrect (should be WR'), types control H to delete it,
(should be kN'), types control H to delete it, and types and types 4R\
4N\
e. The operator types a carriage return to conclude
c. The operator then recognizes that the initial 4D' is
the line.
incorrect also (should be 4E'). Since the character to be
;EQUATES
DELKEY .EQU 018H ;DELETE LINE KEYBOARD CHARACTER
BSKEY .EQU 08H ;BACKSPACE KEYBOARD CHARACTER
10A READ A LINE OF CHARACTERS (RDLINE) 421
RDLINE:
;SAVE PARAMETERS
STA BUFADR+1 ;SAVE HIGH BYTE OF INPUT BUFFER ADDRESS
STY BUFADR ;SAVE LOW BYTE OF INPUT BUFFER ADDRESS
STX BUFLEN ;SAVE MAXIMUM LENGTH
INITIALIZE BUFF
INIT:
LDA #0
STA BUFIDX
;READ LOOP
;READ CHARACTERS
RDLOOP:
JSR RDCHAR ;READ A CHARACTER FROM TflE KEYBOARD
;DOES NOT ECHO
STRCH:
STA (BUFADR),Y ;STORE THE CHARACTER
JSR WRCHAR ;ECHO CHARACTER TO TERMINAL
422 INPUT/OUTPUT
;EXIT SEQUENCE
;ECHO NEW LINE SEQUENCE (USUALLY CR,LF)
;GET LENGTH OF BUFFER
EXITRD:
JSR WRNEWL ;ECHO THE NEW LINE SEQUENCE
LDX BUFIDX ;RETURN THE LENGTH IN X
RTS ;RETURN
**********************************************
;ROUTINE: RDCHAR
;PURPOSE: READ A CHARACTER BUT DO NOT ECHO TO OUTPUT DEVICE
;ENTRY: NONE
;EXIT: REGISTER A = CHARACTER
;REGISTERS USED: ALL
*****************************************
RDCHAR:
JSR OFDOCH ;APPLE MONITOR READ KEYBOARD
AND #O1111111B ;ZERO BIT 7
RTS
******************************************
;ROUTINE: WRCHAR
;PURPOSE: WRITE A CHARACTER TO THE OUTPUT DEVICE
;ENTRY: REGISTER A = CHARACTER
;EXIT: NONE
;REGISTERS USED: ALL
******************************************
WRCHAR:
ORA #10000000B ;SET BIT 7
JSR OFDEDH ;APPLE MONITOR CHARACTER OUTPUT ROUTINE
RTS
******************************************
;ROUTINE: WRNEWL
;PURPOSE: ISSUE THE APPROPRIATE NEW LINE CHARACTER OR
CHARACTERS. NORMALLY, THIS ISA CARRIAGE RETURN
; AND LINE FEED, BUT SOME COMPUTERS (SUCH AS APPLE II)
; REQUIRE ONLY A CARRIAGE RETURN.
;ENTRY: NONE
;EXIT: NONE
;REGISTERS USED: ALL
********************************************
10A READ A LINE OF CHARACTERS (RDLINE) 423
WRNEWL:
JSR 0FD8EH ;ECHO CARRIAGE RETURN AND LINE FEED
RTS
•a***************************************
;ROUTINE: BACKSP
;PURPOSE: PERFORM A DESTRUCTIVE BACKSPACE
;ENTRY: BUFIDX = INDEX TO NEXT AVAILABLE LOCATION IN BUFFER
;EXIT: CHARACTER REMOVED FROM BUFFER
;REGISTERS USED: ALL
.a**************************************
BACKSP:
EXITBS:
RTS
CSRLFT .EQU U8H ^-CHARACTER WHICH MOVES CURSOR LEFT ONE LOCATION
LENBSS: .EQU 3 ;LENGTH OF BACKSPACE STRING
BSSTRG: .BYTE CSRLFT,SPACE,CSRLFT
; DATA
BUFIDX: .BLOCK ;INDEX TO NEXT AVAILABLE CHARACTER IN BUFFER
BUFLEN: .BLOCK ;BUFFER LENGTH
COUNT: .BLOCK 7COUNT FOR BACKSPACE AND RETYPE
INDEX: .BLOCK ;INDEX FOR BACKSPACE AND RETYPE
SAMPLE EXECUTION:
424 INPUT/OUTPUT
SC1001:
;READ LINE
LDA
JSR WRCHAR ;OUTPUT PROMPT (QUESTION MARK)
LDA ADRBUF+1 ;GET THE BUFFER ADDRESS
LDY ADRBUF
LOX #LINBUF ;GET THE BUFFER LENGTH
JSR RDLINE ;READ A LINE
;ECHO LINE
STX CNT ;STORE NUMBER OF CHARACTERS IN THE BUFFER
LDA #0
STA IDX
TLOOP:
LDA CNT
BNE TLOOP1 ;BRANCH IF THERE ARE MORE CHARACTERS TO SEND
JSR WRNEWL ;IF NOT ISSUE NEW LINE (CR,LF)
JMP SC1001 ;AND START OVER
TLOOP1:
LDY IDX
LDA INBUFF,Y ;GET THE NEXT CHARACTER
JSR WRCHAR ;OUTPUT IT
INC IDX
DEC CNT ;DECREMENT LOOP COUNTER
JMP TLOOP
;DATA SECTION
IDX: .BLOCK 1 ;INDEX
CNT: .BLOCK 1 ; COUNTER
ADRBUF: .WORD 1NBUFF ;ADDRESS OF INPUT BUFFER
LINBUF: .EQU 10H ;LENGTH OF INPUT BUFFER
INBUFF: .BLOCK LINBUF ;DEFINE THE INPUT BUFFER
.END ;PROGRAM
Write a Line of Characters to an Output Device
(WRLINE) 10B
Example
Data: Buffer length = 5 Result: 'ENTER' sent to the output device.
Buffer contains * ENTER'
425
426 INPUT/OUTPUT
Exit: None
WRLINE:
;SAVE PARAMETERS
STA BUFADR+1 ;SAVE HIGH BYTE OF OUTPUT BUFFER ADDRESS
STY BUFADR ;SAVE LOW BYTE OF OUTPUT BUFFER ADDRESS
STX BUFLEN ;SAVE LENGTH
BEQ EXIT ;EXIT IF LENGTH = 0
;INITIALIZE BUFFER
LDA #0
STA BUFIDX
WRLOOP:
LDY BUFIDX
LDA (BUFADR),Y ;GET NEXT CHARACTER
JSR WRCHAR ;OUTPUT CHARACTER
INC BUFIDX ;INCREMENT BUFFER INDEX
DEC BUFLEN ;DECREMENT BUFFER LENGTH
BNE WRLOOP ;BRANCH IF NOT DONE
EXIT:
RTS
.••a*******************************************
•A********************************************
10B WRITE A LINE OF CHARACTERS (WRLINE) 427
a****************************************
;ROUTINE: WRCHAR
;PURPOSE: WRITE A CHARACTER TO THE OUTPUT DEVICE
;ENTRY: REGISTER A = CHARACTER
;EXIT: NONE
;REGISTERS USED: ALL
.••••A************************************
WRCHAR:
ORA #1000000UB ;SET BIT 7
JSR OFDEDH ;APPLE MONITOR CHARACTER OUTPUT ROUTINE
RTS
.••a*****************************
; DATA SECTION
BUFIDX: .BLOCK 1 ;INDEX TO NEXT AVAILABLE CHARACTER IN BUFFER
BUFLEN: .BLOCK 1 ;BUFFER LENGTH
SAMPLE EXECUTION:
SC1002:
;READ LINE USING THE APPLE MONITOR GETLN ROUTINE AT 0FD6AH
; 33H = ADDRESS CONTAINING APPLE PROMPT CHARACTER
; 200H = BUFFER ADDRESS
LDA #"?" OR 80H ;USE ? FOR PROMPT WITH BIT 7 SET
STA 033H ;SET UP APPLE PROMPT CHARACTER
JSR 0FD6AH ;CALL APPLE MONITOR GETLN ROUTINE
STX LENGTH ;RETURN LENGTH IN REGISTER X
;DATA SECTION
LENGTH: .BLOCK 1
.END ;PROGRAM
Generate Even Parity (GEPRTY) 10C
Data in the accumulator (bit 7 is not used). Data with even parity in bit 7 in the
accumulator.
Examples
1. Data: (A) = 4216 = 010000102 (ASCII B) 2. Data: (A) = 4316 - - 010000112 (ASCII C)
Result: (A) = 4216 = 010000102 (ASCII B with bit Result: (A) = C3i6 = 110000112 (ASCII C with bit
7 cleared) 7 set)
Even parity is 0, since 010000102 has an
even number (2) of 1 bits.
428
10C GENERATE EVEN PARITY (GEPRTY) 429
GEPRTY:
;SAVE THE DATA
STA VALUE
PLA
TAY
PLA
TAX
LDA VALUE ;GET VALUE WITH PARITY
RTS ;RETURN
;DATA SECTION
VALUE: .BLOCK 1 ;TEMPORARY DATA STORAGE
SAMPLE EXECUTION:
430 INPUT/OUTPUT
;GENERATE PARITY FOR VALUES FROM 0..127 AND STORE THEM IN BUFFER
SC1003:
LDX #0
SC1LP:
TXA
JSR GEPRTY ;GENERATE EVEN PARITY
STA BUFFER,X ;STORE THE VALUE WITH EVEN PARITY
INX
CPX #80H
BNE SC1LP ;BRANCH IF NOT DONE
BRK
.END ;PROGRAM
Check Parity (CKPRTY) 10D
bit shifted into the Carry is 1. The program Data Memory Required: One byte anywhere in
citiits as soon as the shifted data becomes zero RAM (at address VALUE) for the data.
(since zero obviously does not contain any 1
bits). The least significant bit of the count is 0
if the data byte contains an even number of 1
bits and 1 if the data byte contains an odd shifting the least significant bit of the count
number of 1 bits. The program concludes by to the Carry flag.
Data byte in the accumulator (bit 7 is Carry = 0 if the parity of the data byte is
included in the parity generation). even, 1 if the parity is odd.
Examples
1. Data: (A) = 4216 = 010000102 (ASCII B) 2. Data: A) = 4316 = 010000112 (ASCII C)
Result: Carry = 0, since 42,6 (010000102) has Result: Carry = 1, since 4316 (010000112) has
an even number (2) of 1 bits. an odd number (3) of 1 bits.
431
432 INPUT/OUTPUT
CKPRTY:
;SAVE DATA VALUE
STA VALUE
TYA
LSR A ;CARRY FLAG = LSB OF NUMBER OF 1 BITS
SAMPLE EXECUTION:
;CHECK PARITY FOR VALUES FROM 0..255 AND STORE THEM IN BUFFER
;BUFFER [VALUE] = 0 FOR EVEN PARITY
;BUFFER [VALUE] = 1 FOR ODD PARITY
SC1004:
LDX #0
SCLP:
TXA
JSR CKPRTY ;CHECK PARITY
LDA #0
ROL A ;GET PARITY TO BIT 0
STA BUFFER,X ;STORE THE PARITY
INX ;INCREMENT VALUE
BNE SCLP ;CONTINUE THROUGH ALL THE VALUES
BRK
JMP SC1004
.END ;PROGRAM
CRC-16 Checking and Generation (ICRC16,CRC16) 10E
Entry Conditions
434
10E CRC-16 CHECKING AND GENERATION (ICRC16, CRC16, GCRC16) 435
Exit Conditions
1. For ICRC16: zero (initial CRC value) significant byte) and CRC+1 (more signifi
in memory locations CRC (less significant cant byte)
byte) and CRC+1 (more significant byte) 3. For GCRC16: CRC in the accumulator
CRC polynomial in memory locations PLY (more significant byte) and index register Y
(less significant byte) and PLY+1 (more sig (less significant byte).
nificant byte)
2. For CRC 16: CRC with current data
byte included in memory locations CRC (less
Examples
1. Generating a CRC.
Note that only subroutine ICRC16
Call ICRC16 to initialize the polynomial and start the
depends on the particular CRC polynomial
CRC at zero.
Call CRC 16 to update the CRC for each byte of data being used. To change the polynomial
for which the CRC is to be generated. requires only a change of the data that
Call GCRC16 to obtain the resulting CRC (more sig
ICRC16 loads into memory locations PLY
nificant byte in A, less significant byte in Y).
(less significant byte) and PLY 4-1 (more sig
2. Checking a CRC.
nificant byte).
Call ICRC16 to initialize the polynomial and start the
CRC at zero.
Call CRC 16 to update the CRC for each byte of data
(including the stored CRC) for checking.
Call GCRC16 to obtain the resulting CRC (more sig
nificant byte in A, less significant byte in Y). If there
were no errors, both bytes should be zero.
Reference
To generate a CRC:
1) Call ICRC16 to initialize the CRC to 0
and the CRC polynomial.
2) Call CRC16 for each byte of data for
which the CRC is to be generated.
3) Call GCRC16 to get the resulting CRC.
It should then be appended to the data,
high byte first.
To check a CRC:
1) Call ICRC16 to initialize the CRC.
2) Call CRC16 for each byte of data and
the 2 bytes of CRC previously generated.
3) Call GCRC16 to obtain the CRC. It will
be zero if no errors have occurred.
CRC16:
;SAVE THE DATA BYTE
STA VALUE
;BIT 7 IS 1 SO EXC
TAY ;SAVE CRC HIGH IN Y
LDA CRC
EOR PLY ;EXCLUSIVE OR LOW BYTE WITH THE POLYNOMIAL
STA CRC
TYA
EOR PLY+1 ;DO HIGH BYTE ALSO
CRCLP1:
STA CRC+1 ;STORE THE HIGH BYTE OF THE CRC
OEX
BNE CRCLP ;BRANCH IF NOT DONE WITH ALL 8 BITS
;ROUTINE: ICRC16
;PURPOSE: INITIALIZE CRCHI, CRCLO, PLYHI, PLYLO
;ENTRY: NONE
;EXIT: CRC AND POLYNOMIAL INITIALIZED
,-REGISTERS USED: A,F
*******************************************
ICRC16:
LDA #0
STA CRC ;CRC = 0
STA CRC+1
LDA #5
STA PLY ;PLY = 8005H
;8005H IS FOR
; (1 IN EACH POSITION FOR WHICH A POWER
; APPEARS IN THE FORMULA)
LDA #80H
STA PLY+1
RTS
438 INPUT/OUTPUT
********************************************
;ROUTINE: GCRC16
;PURPOSE: GET THE CRC16 VALUE
;ENTRY: NONE
;EXIT: REGISTER A = CRC16 HIGH BYTE
7 REGISTER Y = CRC16 LOW BYTE
;REGISTERS USED: A,F,Y
••••a***************************************
GCRC16:
LDA CRC+1 ;A HIGH BYTE
LDY CRC ;Y LOW BYTE
RTS
SAMPLE EXECUTION:
CRCVAL: BLOCK 2
• END
I/O Device Table Handler (lOHDLR) 10F
440
10F I/O DEVICE TABLE HANDLER (IOHDLR) 441
Subroutine INITDL initializes the device device list, making its address the head of the
list, setting the initial link to zero. list and setting its link field to the old head of
Subroutine ADDDL adds an entry to the the list.
Example
In the example provided, we have the follow
ing structure:
INPUT/OUTPUT OPERATIONS INPUT/OUTPUT CONTROL BLOCK
Operation
Operation Index Contents
Number
0 Device number
0 Initialize device
1 Operation number
1 Determine input status
2 Status
2 Read 1 byte from input device
3
3 Less significant byte of starting address of
Read N bytes from input device (normally
buffer
one line)
4
4 More significant byte of starting address of
Determine output status
buffer
5 Write one byte to output device
5 Less significant byte of buffer length
6 Write N bytes to output device (normally
6 More significant byte of buffer length
one line)
442 INPUT/OUTPUT
IOHDLR:
;SAVE IOCB ADDRESS AND X REGISTER
STA IOCBA+1
STY IOCBA
STX SVXREG
10F I/O DEVICE TABLE HANDLER (lOHDLR) 445
SRCHLP:
;CHECK IF AT END OF DEVICE TABLE LIST (LINK FIELD = 0000)
LDA CURDEV
ORA CURDEV+1
BEQ BADDN ;BRANCH IF NO MORE DEVICES
;ADVANCE TO THE NEXT DEVICE TABLE ENTRY THROUGH THE LINK FIELD
; MAKE CURRENT DEVICE = LINK
LDY #DTLNK
LDA (CURDEV),Y ;GET LOW BYTE OF LINK FIELD
PHA ; SAVE ON STACK
INY
LDA (CURDEV),Y ;GET HIGH BYTE OF LINK FIELD
STA CURDEV+1
PLA ;RECOVER LOW BYTE OF LINK FIELD
STA CURDEV
JMP SRCHLP ;CONTINUE SEARCHING
LDA (CURDEV),Y
STA OPADR ;STORE LOW BYTE
INY
LDA (CURDEV),Y
STA OPADR+1 ;STORE HIGH BYTE
ORA OPADR ;CHECK FOR NON-ZERO OPERATION ADDRESS
BEQ BADOP ;BRANCH IF OPERATION IS INVALID (ADDRESS = 0)
BADDN:
LDA #1 ;ERROR CODE 1 — NO SUCH DEVICE
BNE EREXIT
BADOP:
LDA #2 ;ERROR CODE 2 — NO SUCH OPERATION
EREXIT:
LDY #IOCBST
STA (IOCBA),Y ;STORE ERROR STATUS
RTS
.****************************************
;ROUTINE: INITDL
;PURPOSE: INITIALIZE THE DEVICE LIST TO EMPTY
;ENTRY: NONE
;EXIT: THE DEVICE LIST SET TO NO ITEMS
;REGISTERS USED: A,F
*****************************************
INITDL:
;INITIALIZE DEVICE LIST TO 0 TO INDICATE NO DEVICES
LDA #0
STA DVLST
STA DVLST+1
RTS
*******************************************
;ROUTINE: ADDDL
;PURPOSE: ADD A DEVICE TO THE DEVICE LIST
;ENTRY: REGISTER A = HIGH BYTE OF A DEVICE TABLE ENTRY
; REGISTER Y = LOW BYTE OF A DEVICE TABLE ENTRY
;EXIT: THE DEVICE TABLE ADDED TO THE DEVICE LIST
;REGISTERS USED: ALL
********************************************
ADDDL:
;X,y NEW DEVICE TABLE ENTRY
TAX
;MAKE NEW DEVICE TABLE ENTRY THE HEAD OF THE DEVICE LIST
STY DVLST
STX DVLST+1
;SET LINK FIELD OF THE NEW DEVICE TO THE OLD HEAD OF THE DEVICE LIST
PLA
, LDY #0
STA (DVLST),Y ;STORE THE LOW BYTE
PLA
INY
STA (DVLST),Y ;STORE THE HIGH BYTE
RTS
;DATA SECTION
OPADR: .BLOCK 2 ;OPERATION ADDRESS USED TO VECTOR TO
; SUBROUTINE
SVXREG: .BLOCK 1 ;TEMPORARY STORAGE FOR X REGISTER
SAMPLE EXECUTION:
;EQUATE
CR .EQU 08DH ;APPLE II CARRIAGE RETURN CHARACTER
CBUF .EQU 0D6H ;STARTING ADDRESS OF I/O BUFFER
SC1006:
;INITIALIZE DEVICE LIST
JSR INITDL
STA IOCB+IOCBOP
LDA AIOCB+1 ;SET REGISTERS A,Y TO THE IOCB ADDRESS
LDY AIOCB
JSR IOHDLR ;WRITE 1 BYTE
BRK
JMP SC1006
;BUFFER
LENBUF .EQU 127
BUFFER .BLOCK LENBUF
;CONSOLE INITIALIZE
CINIT:
LDA #0 ;A = STATUS NO ERRORS
RTS ;NO INITIALIZATION NECESSARY
CNONE:
LDA #0 ;NOT READY
CIS1
LDY #IOCBST
STA (IOCBA),Y ;STORE STATUS AND LEAVE IT IN REGISTER A
RTS
;VERIFY THAT THE NUMBER OF BYTES READ WILL FIT INTO THE CALLERS BUFFER
LDY #IOCBBL+1
LDA (IOCBA)fY ;GET HIGH BYTE
BNE CINN1 ;BRANCH IF HIGH BYTE IS NOT ZERO
DEY
TXA
CMP (IOCBA),Y
BCC CINN1 ;BRANCH IF THE NUMBER OF CHARACTERS READ IS
; LESS THAN THE BUFFER LENGTH
BEQ CINN1 ;BRANCH IF THE LENGTHS ARE EQUAL
LDA (IOCBA),Y
TAX ;OTHERWISE TRUNCATE THE NUMBER OF CHARACTERS
: READ TO THE BUFFER LENGTH
CINN1:
TXA
STA (IOCBA),Y ;SET BUFFER LENGTH TO NUMBER OF CHARACTERS READ
LDA #0
INY
STA (IOCBA),Y ;ZERO UPPER BYTE OF BUFFER LENGTH
10F I/O DEVICE TABLE HANDLER (lOHDLR) 451
.a**************************************
;PRINTER ROUTINES
; ASSUME PRINTER CARD IS IN SLOT 1
****************************************
452 INPUT/OUTPUT
;PRINTER INITIALIZE
PINIT:
LDA #0 ;NOTHING TO DO, RETURN NO ERRORS
RTS
.•a***************************************
•ROUTINE: OUTN
;PURPOSE: OUTPUT N CHARACTERS
;ENTRY: REGISTER A = HIGH BYTE OF CHARACTER OUTPUT SUBROUTINE ADDRESS
; REGISTER Y = LOW BYTE OF CHARACTER OUTPUT SUBROUTINE ADDRESS
; IOCBA = STARTING ADDRESS OF AN IOCB
;EXIT: DATA OUTPUT
REGISTERS USED: ALL
a******************************************
OUTN:
;STORE ADDRESS OF THE CHARACTER OUTPUT SUBROUTINE
STA COSR+1
STY COSR
LP1:
;INCREMENT TO THE NEXT CHARACTER IN THE BUFFER
INC IDX
BNE LP2
INC CBUF+1 ;INCREMENT THE HIGH BYTE IS NECESSARY
LP2:
LDA BUFLEN
BNE DECLS
DEC BUFLEN+1 ;BORROW FROM HIGH BYTE IF NECESSARY
DECLS: DEC BUFLEN ;ALWAYS DECREMENT LOW BYTE
BNE OUTLP
LDA BUFLEN+1
BNE OUTLP ;CONTINUE UNLESS ALL CHARACTERS SENT
OUT 3:
RTS
.END
Initialize I/O Ports (IOPORTS) 10G
6551 Asychronous Communications Device Data Memory Required: Four bytes on page 0,
two for a pointer to the array (starting at address
Adapter, and the 6850 Asynchronous Com
ARYADR, 00D016 in the listing) and two for a
munications Device Adapter. pointer to the port (starting at address PRTADR,
This subroutine is intended as a 00D216 in the listing).
2. Initialize output ports to known starting Procedure: The program loops through the
values. specified number of ports, obtaining the port
address and the initial value from the array
3. Enable or disable interrupts from pe
and storing the initial value in the port
ripheral chips.
address. This procedure does not depend on
4. Determine operating modes, such as the type of devices used in the I/O section or
whether inputs are latched, whether strobes on the number of devices. Additions and
are produced, how priorities are assigned, deletions can be made by means of appropri
whether timers operate continuously or only ate changes in the array and in the parameters
on demand, etc. of the routine, without changing the routine
5. Load initial counts into timers. itself.
454
10G INITIALIZE I/O PORTS (IPORTS) 455
Example
Data: Number of ports to initialize = Result: Initial value for port 1 stored in port 1
Array elements are: address
Initial value for port 2 stored in port 2
High byte of port 1 address
address
Low byte of port 1 address
Initial value for port 3 stored in port 3
Initial value for port 1
address.
High byte of port 2 address
Low byte of port 2 address
Note that each element in the array consists
Initial value for port 2
High byte of port 3 address of 3 bytes containing:
Low byte of port 3 address Less significant byte of port address
Initial value for port 3
More significant byte of port address
Exit: None
IPORTS:
;SAVE STARTING ADDRESS OF INITIALIZATION ARRAY
STA ARYADR+1
STY ARYADR
EXITIP:
RTS
SAMPLE EXECUTION:
;INITIALIZE
6520 PIA
6522 VIA
6530 ROM/RAM/IO/TIMER
6532 RAM/IO/TIMER
6850 SERIAL INTERFACE(ACIA)
; 6551 SERIAL INTERFACE(ACIA)
SC1007:
LDA ADRARY+1
LDY ADRARY
LDX SZARY
JSR IPORTS ;INITIALIZE THE PORTS
BRK
ARRAY:
INITIALIZE 6520, ASSUME BASE ADDRESS FOR REGISTERS AT 2000H
; PORT A = INPUT
; CA1 = DATA AVAILABLE, SET ON LOW TO HIGH TRANSITION, NO INTERRUPTS
; CA2 = DATA ACKNOWLEDGE HANDSHAKE
.WORD 2001H ;6520 CONTROL REGISTER A ADDRESS
.BYTE 00000000B ;INDICATE NEXT ACCESS TO DATA DIRECTION
; REGISTER (SAME ADDRESS AS DATA REGISTER)
.WORD 2000H ;6520 DATA REGISTER A ADDRESS
.BYTE 00000000B ;ALL BITS = INPUT
.WORD 2001H ;6520 CONTROL REGISTER A ADDRESS
.BYTE 00100110B ;SET UP CAl,CA2 AND SET BIT 2 TO DATA REGISTER
; PORT B = OUTPUT
; CBl = DATA ACKNOWLEDGE, SET ON HIGH TO LOW TRANSITION, NO INTERRUPTS
; CB2 = DATA AVAILABLE, CLEARED BY WRITING DATA REGISTER B
SET TO 1 BY HIGH TO LOW TRANSITION ON CBl
458 INPUT/OUTPUT
; 1 STOP BIT
; DIVIDE MASTER CLOCK BY 1
; NOINTERRUPTS
.WORD 2050H ;WRITE TO 6850 CONTROL REGISTER
.BYTE 00000011B ;PERFORM A MASTER RESET
.WORD 2050H ;6850 CONTROL REGISTER
.BYTE 00010101B ;NO INTERRUPTS, RTS LOW,
;8 BITS, 1 STOP, DIVIDE BY 1
• END ;PROGRAM
Delay Milliseconds (DELAY) 10H
(Y) = Number of milliseconds to delay Returns after the specified number of milli
(1 to 255). seconds with (X) = (Y) = 0.
Example
Data: (Y) = number of milliseconds = 2A,6 = 42,0
Result: Software delay of 2A16 (4210) milliseconds,
assuming that user supplies the proper value
of MSCNT.
460
10H DELAY MILLISECONDS (DELAY) 461
; HERE IS THE FORMULA FOR COMPUTING THE DELAY COUNTS MSCNTl AND MSCNT2
25 CYCLES OVERHEAD
EQUATES
1 MHZ CLOCK
MSCNT .EQU 0C6H ;198 TIMES THROUGH DELAYl
DELAY:
CPY #0 ; 2 CYCLES
BEQ EXIT ; 2 CYCLES (EXIT IF DELAY = 0)
NOP
; 2 CYCLES (TO MAKE OVERHEAD =25 CYCLES)
462 INPUT/OUTPUT
LASTl:
;DELAY THE LAST TIME 25 CYCLES LESS TO TAKE THE
; CALL, RETURN, AND ROUTINE OVERHEAD INTO ACCOUNT
LDX #MSCNT-3 ; 2 CYCLES
DELAY2:
DEX ; 2 CYCLES
BNE DELAY2 ; 3 CYCLES
EXIT:
RTS ; 6 CYCLES
SAMPLE EXECUTION:
SC1008:
;
;DELAY 10 SECONDS
; CALL DELAY 40 TIMES AT 250 MILLISECONDS EACH
LDA #40 ;40 TIMES (28 HEX)
STA COUNT
;DA0?A SECTION
COUtiT .BYTE 0
.END ;PROGRAM
Unbuffered Interrupt-Driven Input/Output
Using a 6850 ACIA (SINTIO) 11A
buffer. 5. INIT A, F
Execution Time:
2. INST determines whether there is a
1. INCH 33 cycles if a character is available
character available in the input buffer.
2. INST 12 cycles
3. OUTCH writes a character into the out 3. OUTCH 92 cycles if the output
put buffer. buffer is empty and the ACIA is ready to send
data
4. OUTST determines whether the output 4. OUTST 12 cycles
buffer is full. 5. INIT 73 cycles
6. IOSRVC 39 cycles to service an input
5. INIT initializes the 6850 ACIA, the interrupt, 59 cycles to service an output interrupt,
interrupt vectors, and the software flags 24 cycles to determine interrupt is from another
(used to transfer data between the main pro device
gram and the interrupt service routine). Program Size: 168 bytes
464
11A UNBUFFERED INTERRUPT-DRIVEN I/O USING A 6850 ACIA (SINTIO) 465
1 of the ACIA status register = 1), or the 2. Output data becomes available. That is,
product of some other device. If the input the system now has data to transmit. But
interrupt occurred, the program reads the there is no use sitting back and waiting for the
data, saves it in memory, and sets the Data output interrupt, since it has been disabled.
Ready flag (RECDF). If the output interrupt 3. The main program calls the routine
occurred, the program determines whether (OUTDAT) that sends data to the ACIA.
data is available. If not, the program simply Checking the ACIA's status shows that it is,
disables the output interrupt. If data is availa in fact, ready to transmit a character (it told
ble, the program sends it to the ACIA, clears us it was when the output interrupt occur
the Character Available flag (TRNDF), and red). The routine then sends the character
enables both the input and the output inter and reenables the interrupts.
rupts.
The basic problem here is that output
The only special problem in using these
devices may request service before the com
routines is that an output interrupt may occur
puter is ready for them. That is, the devices
when no data is available. We cannot ignore
can accept data but the computer has nothing
the interrupt or it will assert itself
to send. In particular, we have an initializa
indefinitely, creating an endless loop. The
tion problem caused by output interrupts
solution is to disable output interrupts. But
asserting themselves and expecting service.
now we create a new problem when data is
Input devices, on the other hand, request
ready to be sent. That is, if we have disabled
service only when they have data. They start
output interrupts, the system cannot learn
out in the not ready state; that is, an input
from an interrupt that the ACIA is ready to
device has no data to send initially, while the
transmit. The solution to this is to create an
computer is ready to accept data. Thus output
additional, non-interrupt-driven entry to the
devices cause more initialization and
routine that sends a character to the ACIA.
sequencing problems in interrupt-driven
Since this entry is not caused by an interrupt,
systems than do input devices.
we must check the ACIA to see that its out
Our solution may, however, result in an
put register is actually empty before sending
odd situation. Let us assume that the system
it a character.
has some data ready for output but the ACIA
The special sequence of operations is the
is not yet ready for it. Then the system must
following:
wait with interrupts disabled for the ACIA to
1. Output interrupt occurs before new become ready; that is, an interrupt-driven
data is available (that is, the ACIA becomes system must disable its interrupts and wait
ready for data). The response is to disable the idly, polling the output device. We could
output interrupt, since there is no data to be eliminate this drawback by keeping a soft
sent. Note that this sequence will not occur ware flag that would be changed when the
initially, since INIT disables the output inter output interrupt occurred at a time when
rupt. Otherwise, the output interrupt would there was no data. Then the system could
occur immediately, since the ACIA surely check the software flag and determine
starts out empty and therefore ready to whether the output interrupt had already
transmit data. occurred. (See Subroutine 11C.)
466 INTERRUPTS
INCH
Read a character.
INST
Determine input status (whether the input
buffer is empty).
OUTCH
Write a character.
OUTST
Determine output status (whether the output
buffer is full).
INIT
Initialize.
Entry: INCH
No parameters.
INST
No parameters.
OUTCH
Register A = character to transmit
OUTST
No parameters.
INIT
No parameters.
Exit: INCH
Register A ■ character.
INST
Carry flag equals 0 if input buffer is empty,
1 if character is available.
11A UNBUFFERED INTERRUPT-DRIVEN I/O USING A 6850 ACIA (SINTIO) 467
OUTCH
No parameters
OUTST
Carry flag equals 0 if output buffer is
empty, 1 if it is full.
INIT '
No parameters.
Time: INCH ?
33 cycles if a character is available ;
INST 1
12 cycles ?
OUTCH ;
92 cycles if the output buffer is empty and
the ACIA is ready to transmit ;
OUTST
12 cycles ?
INIT ;
73 cycles ?
IOSRVC ;
24 cycles minimum if the interrupt is not ours;
39 cycles to service a input interrupt ;
59 cycles to service a output interrupt ;
;
Size: Program 168 bytes ;
Data 6 bytes
;EXAMPLE 6850 ACIA PORT DEFINITIONS FOR AN APPLE SERIAL BOARD IN SLOT 1
ACIASR .EQU 0C094H ;ACIA STATUS REGISTER
ACIADR • EQU 0C095H ;ACIA DATA REGISTER
ACIACR .EQU 0C094H ;ACIA CONTROL REGISTER
IRQVEC .EQU 03FEH ;APPLE IRQ VECTOR ADDRESS
;READ A CHARACTER
INCH:
JSR INST ;GET INPUT STATUS
BCC INCH ;WAIT IF CHARACTER IS NOT AVAILABLE
PHP ;SAVE CURRENT STATE OF INTERRUPT SYSTEM
SEI ;DISABLE INTERRUPTS
LDA #0
STA RECDF ;INDICATE BUFFER IS NOW EMPTY
LDA RECDAT ;GET THE CHARACTER FROM THE BUFFER
PLP ;RESTORE FLAGS
468 INTERRUPTS
RTS
;WAIT FOR THE CHARACTER BUFFER TO EMPTY, THEN STORE THE NEXT CHARACTER
WAITOC:
JSR OUTST ;GET THE OUTPUT STATUS
BCS WAITOC ;WAIT IF THE OUTPUT BUFFER IS FULL
SEI ;DISABLE INTERRUPTS WHILE LOOKING AT THE
; SOFTWARE FLAGS
PLA ;GET THE CHARACTER
STA TRNDAT ;STORE THE CHARACTER
LDA #0FFH ;INDICATE CHARACTER AVAILABLE (BUFFER FULL)
STA TRNDF
JSR OUTDAT ;SEND THE DATA TO THE PORT
PLP ;RESTORE FLAGS
RTS
;INITIALIZE
INIT:
PHP ;SAVE CURRENT STATE OF FLAGS
SEI ;DISABLE INTERRUPTS DURING INITIALIZATION
DIVIDE BY 16
8 DATA BITS
2 STOP BITS
OUTPUT INTERRLPTS DISABLED (NOTE THIS)
INPUT INTERRUPTS ENABLED
; REQUESTS SERVICE)
NODATA:
LDA #1OO1OOO1B ;DISABLE OUTPUT INTERRUPTS, ENABLE INPUT
; INTERRUPTS, 8 DATA BITS, 2 STOP BITS,^DIVIDE
; BY 16 CLOCK
STA ACIACR ;TURN OFF OUTPUT INTERRUPTS
EXIT:
PLA ;RESTORE REGISTER A
RTI ;RETURN FROM INTERRUPT
**************************************
;DATA SECTION
RECDAT .BLOCK ;RECEIVE DATA
RECDF BLOCK ;RECEIVE DATA FLAG (0 = NO DATA, FF = DATA)
TRNDAT BLOCK ;TRANSMIT DATA
TRNDF BLOCK ;TRANSMIT DATA FLAG (0 = BUFFER EMPTY,
; FF = BUFFER FULL)
NEXTSR .BLOCK 2 ;ADDRESS OF THE NEXT INTERRUPT SERVICE ROUTINE
SAMPLE EXECUTION:
SC1101:
JSR INIT INITIALIZE
CLI ;ENABLE INTERRUPTS
11A UNBUFFERED INTERRUPT-DRIVEN I/O USING A 6850 ACIA (SINTIO) 471
;SIMPLE EXAMPLE
LOOP:
JSR INCH ;READ A CHARACTER
PHA
JSR OUTCH ;ECHO IT
PLA
CMP #1BH ;IS IT AN ESCAPE CHARACTER ?
BNE LOOP ;STAY IN LOOP IP NOT
BRK
DONE:
BRK
JMP SC1101
• END ;PROGRAM
Unbuffered Interrupt-Driven Input/Output
Using a 6522 VIA (PINTIO) 11B
3. OUTCH waits for the output buffer to 5. INIT clears the software flags, sets up
be emptied, places the character (from the the interrupt vector, and initializes the 6522
accumulator) in the buffer, and sets the VIA. It makes port A an input port, port B an
character available (buffer full) flag output port, control lines CA1 and CB1
(TRNDF). If an unserviced output interrupt active low-to-high, control line CA2 a brief
472
11B UNBUFFERED INTERRUPT-DRIVEN I/O USING A 6522 VIA (PINTIO) 473
output pulse indicating input acknowledge ignore the interrupt or it will assert itself
(active-low after the CPU reads the data), indefinitely, creating an endless loop. The
and control line CB2 a write strobe (active- solution is to simply clear the interrupt by
low after the CPU writes the data and lasting reading the data register in port B. But now
until the peripheral becomes ready again). we create a new problem when the main pro
INIT also enables the input interrupt on CA1 gram has data ready to be sent. The interrupt
and the output interrupt on CB1. indicating that the output device is ready has
no data was available). If data is available, the put interrupt has already occurred (a 0 value
program sends it from the output buffer to indicates it has, FF hex that it has not).
Note that we can clear a VIA interrupt
the VIA, clears the Character Available flag
(TRNDF), sets the Output Interrupt flag without actually sending any data. We cannot
(OIE), and enables both the input and the do this with a 6850 ACIA (see Subroutines
output interrupts. 11A and 11C), so the procedures there are
somewhat different. This problem of unser-
The only special problem in using these viced interrupts occurs only with output
routines is that an output interrupt may occur devices, since input devices request service
when no data is available to send. We cannot only when they have data ready to transfer.
474 INTERRUPTS
;
Purpose: This program consists of 5 subroutines which ;
perform interrupt driven input and output using ;
a 6522 VIA. ;
INCH
Read a character.
INST
Determine input status (whether the input
buffer is empty)•
OUTCH
Write a character.
OUTST
Determine output status (whether the output
buffer is full).
INIT
Initialize.
Entry: INCH
No parameters.
INST
No parameters.
OUTCH
Register A ■ character to transmit
OUTST
No parameters.
INIT
No parameters.
Exit: INCH
Register A « character.
INST
11B UNBUFFERED INTERRUPT-DRIVEN I/O USING A 6522 VIA (PINTIO) 475
Time: INCH
33 cycles if a character is available ;
INST ;
12 cycles ;
OUTCH
83 cycles if the output buffer is empty and ;
the VIA is ready to transmit ;
OUTST ;
12 cycles ;
INIT ;
93 cycles ;
iosrvc ;
24 cycles minimum if the interrupt is not ours;
43 cycles to service a input interrupt ;
81 cycles to service a output interrupt ;
}
;READ A CHARACTER
476 INTERRUPTS
INCH:
JSR INST ;GET INPUT STATUS
BCC INCH ;WAIT IF CHARACTER IS NOT AVAILABLE
PHP ;SAVE CURRENT STATE OF INTERRUPT SYSTEM
SEI ;DISABLE INTERRUPTS
LDA RECDAT ;GET THE CHARACTER FROM THE BUFFER
LDA #0
STA RECDF ;INDICATE BUFFER IS NOW EMPTY
LDA RECDAT ;GET THE CHARACTER FROM THE BUFFER
PLP ;RESTORE FLAGS
RTS
;WRITE A CHARACTER
OUTCH:
PHP ;SAVE STATE OF INTERRUPT FLAG
PHA ;SAVE CHARACTER TO OUTPUT
;WAIT FOR THE CHARACTER BUFFER TO EMPTY, THEN STORE THE NEXT CHARACTER
WAITOC:
JSR OUTST ;GET THE OUTPUT STATUS
BCS WAITOC ;WAIT IF THE OUTPUT BUFFER IS FULL
SEI ;DISABLE INTERRUPTS WHILE LOOKING AT THE
; SOFTWARE FLAGS
PLA ;GET THE CHARACTER
STA TRNDAT ;STORE THE CHARACTER
LDA #0FFH ;INDICATE CHARACTER AVAILABLE (BUFFER FULL)
STA TRNDF
LDA OIE ;HAS THE OUTPUT DEVICE ALREADY REQUESTED
; SERVICE?
BNE OUTCH1 ; NO, BRANCH AND WAIT FOR AN INTERRUPT
JSR OUTDAT ; YES, SEND THE DATA TO THE PORT NOW
OUTCH1: PLP ;RESTORE FLAGS
RTS
;INITIALIZE
INIT:
PHP ;SAVE CURRENT STATE OF FLAGS
SEI ;DISABLE INTERRUPTS
LDA #0
STA RECDF ;NO INPUT DATA AVAILABLE
STA TRNDF ;OUTPUT BUFFER EMPTY
LDA #0FFH ;OUTPUT DEVICE HAS NOT REQUESTED SERVICE
STA OIE
STA VIAIER
PLP ;RESTORE CURRENT STATE OF THE FLAGS
RTS
NODATA:
LDA VIABDR ;READ THE PORT B DATA REGISTER TO CLEAR THE
; INTERRUPT.
LDA #0 ;INDICATE OUTPUT INTERRUPT HAS OCCURRED
STA OIE ; BUT HAS NOT BEEN SERVICED
EXIT:
PLA ;RESTORE REGISTER A
RTI ;RETURN FROM INTERRUPT
****************************************
;ROUTINE: OUTDAT
;PURPOSE: SEND A CHARACTER TO THE VIA
;ENTRY: TRNDAT = CHARACTER TO SEND
;EXIT: NONE
REGISTERS USED: A,F
*****************************************
OUTDAT:
LDA TRNDAT ;GET THE CHARACTER
STA VIABDR ;OUTPUT DATA TO PORT B
LDA #0
STA TRNDF ;INDICATE BUFFER EMPTY
LDA #0FFH
STA OIE ;INDICATE NO UNSERVICED OUTPUT INTERRUPT
RTS
11B UNBUFFERED INTERRUPT-DRIVEN I/O USING A 6522 VIA (PINTIO) 479
;DATA SECTION
RECDAT .BLOCK ;RECEIVE DATA
RECDF BLOCK ;RECEIVE DATA FLAG (0 = NO DATA, FF = DATA)
TRNDAT BLOCK ;TRANSMIT DATA
TRNDF .BLOCK ;TRANSMIT DATA FLAG (0 BUFFER EMPTY
FF BUFFER FULL)
OIE .BLOCK 1 ;OUTPUT INTERRUPT FLAG
; (0 = INTERRUPT OCCURRED WITHOUT SERVICE
; FF = INTERRUPT SERVICED)
NEXTSR .BLOCK 2 ;ADDRESS OF THE NEXT INTERRUPT SERVICE ROUTINE
SAMPLE EXECUTION:
SC1102:
JSR INIT INITIALIZE
CLI ;ENABLE INTERRUPTS
; SIMPLE EXAMP
LOOP:
JSR INCH ;READ A CHARACTER
PHA
JSR OUTCH ;ECHO IT
PLA
CMP #1BH ;IS IT AN ESCAPE CHARACTER ?
BNE LOOP ;STAY IN LOOP IF NOT
BRK
;GET A C:haracter
JSR INST ;IS INPUT DATA AVAILABLE ?
BCC ASYNLP ;BRANCH IF NOT (SEND ANOTHER "A"
JSR INCH ;GET THE CHARACTER
CMP #1BH ;IS IT AN ESCAPE CHARACTER ?
BEQ DONE ;BRANCH IF IT IS
JSR OUTCH ;ELSE ECHO IT
JMP ASYNLP ;AND CONTINUE
DONE:
BRK
JMP SC1102
.END ;PROGRAM
Buffered Interrupt-Driven Input/Output
Using a 6850 ACIA (SINTB) 11C
480
11C BUFFERED INTERRUPT-DRIVEN I/O USING A 6850 ACIA (SINTB) 481
value in its control register. INIT enables the routine must send the data in the same order
input interrupt and disables the output inter that the main program stores it. Thus we
rupt. It does, however, clear the output inter have the following requirements for handling
rupt enable flag, thus indicating that the input:
ACIA is ready to transmit data, although it
1. The main program must know whether
cannot cause an output interrupt.
there is anything in the input buffer.
6. IOSRVC determines whether the inter
2. If the input buffer is not empty, the
rupt was an input interrupt (bit 0 of the ACIA
main program must know where the oldest
status register = 1), an output interrupt (bit
character is (that is, the one that was received
1 of the ACIA status register = 1), or the
first).
product of some other device. If the input
interrupt occurred, the program reads the 3. The input interrupt service routine
data and determines if there is room for it in must know whether the input buffer is full.
the buffer. If there is room, the processor 4. If the input buffer is not full, the input
stores the character at the tail of the input interrupt service routine must know where
buffer, moves the tail of the buffer up one the next empty place is (that is, it must know
position, and increases the input buffer where it should store the new character).
counter by 1. If the output interrupt oc
The output interrupt service routine and
curred, the program determines whether
the main program have a similar set of
there is any data in the output buffer. If there
requirements for the output buffer, although
is none, the program disables the output
the roles of sender and receiver are reversed.
interrupt (so it will not interrupt repeatedly)
We meet requirements 1 and 3 by main
and clears an Output Interrupt flag that indi
taining a counter ICNT. INIT initializes
cates the ACIA is actually ready. The flag lets
ICNJ to zero, the interrupt service routine
the main program know that the ACIA is
adds 1 to it whenever it receives a character
ready even through it cannot declare its
(assuming the buffer is not full), and the
readiness by forcing an interrupt. If there is
main program subtracts 1 from it whenever it
data in the output buffer, the program
removes a character from the buffer (assum
obtains a character from the head of the
ing the buffer is not empty). Thus the main
buffer, sends it to the ACIA, moves the head
program can determine whether the input
of the buffer up one position, and decreases
buffer is empty by checking if ICNT is zero.
the output buffer counter by 1. It then ena
Similarly, the interrupt service routine can
bles both input and output interrupts and sets
determine whether the input buffer is full by
the Output Interrupt flag (in case that flag
checking if ICNT is equal to the size of the
had been cleared earlier).
buffer.
The new problem that occurs in using We meet requirements 2 and 4 by main
multiple-character buffers is the manage taining two indexes, IHEAD and ITAIL,
ment of queues. The main program must defined as follows:
read the data in the same order in which the
input interrupt service routine receives it. 1. ITAIL is the index of the next empty
Similarly, the output interrupt service location in the buffer.
482 INTERRUPTS
2. IHEAD is the index of the oldest characters at one end (the tail) while the
character in the buffer. main program removes them from the other
end (the head). The occupied part of the
INIT initializes IHEAD and ITAIL to buffer thus could start and end anywhere. If
zero. Whenever the interrupt service routine either IHEAD or ITAIL reaches the physical
receives a character, it places it in the buffer end of the buffer, we simply set it back to
at index ITAIL and increments ITAIL by 1 zero. Thus we allow wraparound on the
(assuming that the buffer is not full). buffer; that is, the occupied part of the buffer
Whenever the main program reads a could start near the end (say, at byte #195 of
character, it removes it from the buffer at a 200-byte buffer) and continue back to the
index IHEAD and increments IHEAD by 1 beginning (say, to byte #10). Thus IHEAD
(assuming that the buffer is not empty). would be 195, ITAIL would be 10, and the
Thus IHEAD "chases" ITAIL across the buffer would contain 15 characters occupying
buffer with the service routine entering bytes #195 through 199 and 0 through 9.
4. OUTST: none
3. OUTCH: none
5. INIT: none
4. OUTST: Carry Hag = 0 if output
buffer is not full, 1 if it is full
5. INIT: none
INCH
Read a character.
11C BUFFERED INTERRUPT-DRIVEN I/O USING A 6850 ACIA (SINTB) 483
INST
Determine input status (whether a character
is available).
OUTCH
Write a character.
OUTST
Determine output status (whether the output
buffer is full).
INIT
Initialize.
Entry: INCH
No parameters.
INST
No parameters.
OUTCH
Register A = character to transmit
OUTST
No parameters.
INIT
No parameters.
Exit: INCH
Register A = character.
INST
Carry flag equals 0 if no characters are
available, 1 if character is available.
OUTCH
No parameters
OUTST
Carry flag equals 0 if output buffer is
empty, 1 if it is full.
INIT
No parameters.
A,F
Time: INCH
70 cycles if a character is available
INST
18 cycles
OUTCH
75 cycles minimum, if the output buffer is
not full and the ACIA is ready to transmit
OUTST
12 cycles
INIT
89 cycles
IOSRVC
27 cycles minimum if the interrupt is not ours;
73 cycles to service a input interrupt
102 cycles to service a output interrupt
Buffers:
The routines assume two buffers starting at
addresses IBUF and OBUF. The lengths of the
buffers in bytes are IBSZ and OBSZ. For the
input buffer, IHEAD is the index of the oldest
character (the next one the main program should
read), ITAIL is the index of the next empty
element (the next one the service routine
should fill), and ICNT is the number of bytes
currently filled with characters. For the
output buffer, OHEAD is the index of the oldest
character (the next one the service routine
should send), OTAIL is the index of the next
empty element (the next one the main program
should fill), and OCNT is the number of bytes
currently filled with characters.
;EXAMPLE 6850 ACIA PORT DEFINITIONS FOR AN APPLE SERIAL BOARD IN SLOT 1
11C BUFFERED INTERRUPT-DRIVEN I/O USING A 6850 ACIA (SINTB) 485
;READ A CHARACTER
INCH:
JSR INST ;IS A CHARACTER AVAILABLE ?
BCC INCH ;BRANCH IF NOT
PHP ;SAVE CURRENT STATE OF INTERRUPTS
SEI ;DISABLE INTERRUPTS
LDY I HEAD
LDA IBUF,Y ;GET CHARACTER AT HEAD OF BUFFER
INY
CPY #IBSZ ;DO WE NEED WRAPAROUND IN BUFFER ?
BCC INCH1 ;BRANCH IF NOT
LDY #0 ;ELSE SET HEAD BACK TO ZERO
INCH1:
STY IHEAD
DEC ICNT ;DECREMENT CHARACTER COUNT
PLP ;RESTORE FLAGS
RTS
;WRITE A CHARACTER
OUTCH:
PHP ;SAVE STATE OF INTERRUPT FLAG
PHA ;SAVE CHARACTER TO OUTPUT
;WAIT UNTIL TH
WAITOC :
STY OTAIL
INC OCNT ;INCREMENT BUFFER COUNTER
LDA OIE ;ARE INTERRUPTS DISABLED BUT THE ACIA IS
; ACTUALLY READY ?
BNE OUTCH2 ;EXIT IF ACIA INTERRUPTS NOT READY AND ENABLED
486 INTERRUPTS
JSR OUTDAT ;ELSE SEND THE DATA TO THE PORT AND ENABLE
; INTERRUPTS
0UTCH2:
PLP ;RESTORE FLAGS
RTS
; OUTPUT STATUS
OUTST:
LDA OCNT
CMP #OBSZ ;IS OUTPUT BUFFER FULL ?
; IF OCNT >= OBSZ THEN
CARRY 1 INDICATING THAT THE OUTPUT
BUFFER IS FULL
ELSE
CARRY = INDICATING THAT THE CHARACTER
CAN BE PLACED IN THE BUFFER
•RTS
; INITIALIZE
INIT:
PHP ;SAVE CURRENT STATE OF FLAGS
SEI ;DISABLE INTERRUPTS
;INITIALIZE T
LDA #0
STA ICNT ;NO INPUT DATA
STA IHEAD
STA ITAIL
STA OCNT ;NO OUTPUT DATA
STA OHEAD
STA OTAIL
STA OIE ;ACIA IS READY TO TRANSMIT (NOTE THIS M)
NODATA:
LDA #1001000lB ;DISABLE OUTPUT INTERRUPTS, ENABLE INPUT
; INTERRUPTS, 8 DATA BITS, 2 STOP BITS, DIVIDE
; BY 16 CLOCK
STA ACIACR ;TURN OFF INTERRUPTS
LDA #0
STA OIE ;INDICATE OUTPUT INTERRUPTS ARE DISABLED
; BUT ACIA IS ACTUALLY READY
EXIT:
PLA
TAY ;RESTORE REGISTER Y
PLA ;RESTORE REGISTER A
RTI ;RETURN FROM INTERRUPT
.•••A*********************************
;ROUTINE: OUTDAT
;PURPOSE: SEND A CHARACTER TO THE ACIA FROM THE OUTPUT BUFFER
;ENTRY: OHEAD IS THE INDEX INTO OBUF OF THE CHARACTER TO SEND
;EXIT: NONE
;REGISTERS USED: A,F
OUTDAT:
LDA ACIASR
AND #00000010B ;IS ACIA OUTPUT REGISTER EMPTY ?
BEQ OUTDAT ;BRANCH IF NOT EMPTY (BIT X = 0)
LDY OHEAD
LDA OBUF,Y ;GET THE CHARACTER FROM THE BUFFER
STA ACIADR ;SEND THE DATA
INY
CPY #OBSZ ;DO WE NEED WRAPAROUND ON THE BUFFER ?
BCC OUTD1 ;BRANCH IF NOT
LDY #0 ;ELSE SET HEAD BACK TO ZERO
OUTD1:
STY OHEAD ;SAVE NEW HEAD INDEX
DEC OCNT ;DECREMENT OUTPUT BUFFER COUNTER
LDA #1011000lB
STA ACIACR ;ENABLE 6850 OUTPUT AND INPUT INTERRUPTS,
; 8 DATA BITS, 2 STOP BITS, DIVIDE BY 16 CLOCK
LDA #0FFH
STA OIE ;INDICATE THE OUTPUT INTERRUPTS ARE ENABLED
RTS
; DATA SECTION
ICNT .BLOCK 1 ;INPUT BUFFER COUNTER
I HEAD .BLOCK 1 ;INDEX TO HEAD OF INPUT BUFFER
ITAIL .BLOCK 1 ;INDEX TO TAIL OF INPUT BUFFER
OCNT .BLOCK 1 ;OUTPUT BUFFER COUNTER
OHEAD .BLOCK 1 ;INDEX TO HEAD OF OUTPUT BUFFER
OTAIL .BLOCK 1 ;INDEX TO TAIL OF OUTPUT BUFFER
OIE .BLOCK 1 ;OUTPUT INTERRUPT ENABLE FLAG
SAMPLE EXECUTION:
SC1103:
JSR INIT INITIALIZE
CLI ;ENABLE INTERRUPTS
;SIMPLE EXAMPLE
LOOP:
JSR INCH ;READ A CHARACTER
PHA
JSR OUTCH ;ECHO IT
PLA
CMP #1BH ;IS CHARACTER AN ESCAPE ?
BNE LOOP ;BRANCH IF NOT, CONTINUE LOOPING
BRK
DONE:
BRK
.END ;PROGRAM
Real-Time Clock and Calendar (CLOCK) 11D
490
11D REAL-TIME CLOCK AND CALENDAR (CLOCK) 491
Examples
These examples assume that the tick rate Result (after the tick): March 8, 1982
is DTICK Hz (less than 256 Hz - typical 12:00.00 and DTICK ticks
values would be 60 Hz or 100 Hz) and that That is,
the clock and calendar are saved in memory (TICK) = DTICK
locations (SEC) = 0
(MIN) = 0
TICK number of ticks remaining before a (HOUR) = 0
carry occurs, counted down from (DAY) = 08
DTICK (MONTH) - 03
SEC seconds (0 to 59) (YEAR) = 1982
MIN minutes (0 to 59)
HOUR hour of day (0 to 23) 2. Starting values are Dec. 31, 1982.
DAY day of month (1 to 28, 30, or 31, 11:59.59 p.m. and 1 tick left
depending on month)
That is,
MONTH month of year (1 through 12 for
January through December) (TICK) = l
YEAR& (SEC) = 59
YEAR+1 current year (MIN) = 59
(HOUR) = 23
1. Starting values are March 7, 1982. (DAY) = 31
11:59.59 and 1 tick left. (MONTH) - 12
That is, (YEAR) = 1982
(TICK) = 1
(SEC) = 59
Result (after the tick): Jan. 1, 1983.
(MIN) = 59 12:00.00 a.m. and DTICK ticks
(HOUR) = 23 That is,
(DAY) = 07 (TICK) = DTICK
(MONTH) = 03 (SEC) =0
(YEAR) = 1982 (MIN) = 0
(HOUR) = 0
(DAY) = 1
(MONTH) = 1
(YEAR) = 1983
492 INTERRUPTS
CLOCK
Returns the address of the clock variables
ICLK
Initialize the clock interrupt
Entry: CLOCK
None
ICLK
None
Exit: CLOCK
Register A High byte of the address of the
time variables.
Register Y Low byte of the address of the
time variables.
ICLK
None
Time: CLOCK
14 cycles
ICLCK
166 cycles
CLKINT
22 cycles minimum if the interrupt is not ours;
33 cycles normally if decrementing tick
184 cycles maximum if changing to a new year
;EXIT
PLP ;RESTORE FLAGS
RTS
TXA
PHA
;INCREMENT SECONDS
INC SEC
LOA SEC
CMP #60 ;SECONDS = 60 ?
BCC EXIT ;EXIT IF LESS THAN 60 SECONDS
LDY #0 ;ELSE
STY SEC ; ZERO SECONDS, GO TO NEXT MINLjTE
;INCREMENT MINUTES
INC MIN
LDA MIN
CMP #60 ;MINUTES = 60 ?
BCC EXIT ;EXIT IF LESS THAN 60 MINUTES
STY MIN ;ELSE
; ZERO MINUTES, GO TO NEXT HOUR
;INCREMENT HOURS
INC HOUR
LDA HOUR
CMP #24 ;HOURS = 24 ?
BCC EXIT ;EXIT IF LESS THAN 24 HOURS
STY HOUR ;ELSE
; ZERO HOURS, GO TO NEXT DAY
;INCREMENT DAYS
INC DAY
LDA DAY
LDX MONTH ;GET CURRENT MONTH
CMP LASTDY-1,X ;DAY = LAST DAY OF THE MONTH ?
BCC EXIT ;EXIT IF LESS THAN LAST DAY
INCMTH:
LDY #1
STY DAY ;CHANGE DAY TO 1, INCREMENT MONTH
INC MONTH
LDA MONTH
CMP #13 ;DONE WITH DECEMBER ?
BCC EXIT ;EXIT IF NOT
STY MONTH ;ELSE
; CHANGE MONTH TO 1 (JANUARY)
11D REAL-TIME CLOCK AND CALENDAR (CLOCK) 495
;INCREMENT YEAR
INC YEAR ;INCREMENT LOW BYTE
BNE EXIT
INC YEAR+1 ;INCREMENT HIGH BYTE
EXIT:
;RESTORE REGISTERS
PLA
TAX
PLA
TAY
EXIT1:
PLA
RTI ;RETURN FROM INTERRUPT
; CLOCK VARIABLES
ACVAR: .WORD CLKVAR ;BASE ADDRESS OF CLOCK VARIABLES
CLKVAR:
TICK: .BLOCK 1 ;TICKS LEFT IN CURRENT SECOND
SEC: .BLOCK 1 ;SECONDS
MIN: .BLOCK 1 ;MINUTES
HOUR: .BLOCK 1 ;HOURS
DAY: .BLOCK 1 ;DAY = 1 THROUGH NUMBER OF DAYS IN A MONTH
MONTH: .BLOCK 1 ;MONTH 1=JANUARY .. 12=DECEMBER
YEAR: .WORD 0 ;YEAR
;DEFAULTS
DFLTS:
DTICK: .BYTE 60 ;DEFAULT TICK (60HZ INTERRUPT)
DSEC: .BYTE 0 ;DEFAULT SECONDS
DMIN: .BYTE 0 ;DEFAULT MINUTES
DHR: .BYTE 0 ;DEFAULT HOURS
DDAY: .BYTE 1 ;DEFAULT DAY
DMTH: .BYTE 1 •DEFAULT MONTH
DYEAR: .WORD 1981 .•DEFAULT YEAR
SAMPLE EXECUTION:
This routine prompts the operator for an initial date and time,
it then continuously displays the date and time in the center of
the screen.
The operator may use the escape key to abort the routine. Any
other key will reprompt for another initial date and time.
SC1104:
JSR ICLK INITIALIZE
RDTIME:
JSR RDLINE ;READ A LINE INTO THE APPLE LINE BUFFER AT
; 200H. RETURNS WITH LENGTH IN X
;GET MONTH
JSR NXTNUM ;GET NEXT NUMBER FROM INPUT LINE
LDY #OMTH
STA (CVARS),Y ;SET MONTH
;GET DAY
JSR NXTNUM
LDY #ODAY
STA (CVARS),Y
;GET YEAR
JSR NXTNUM
LDY #OYEAR
STA (CVARS),Y
CLC
ADC CEN20 ;ADD 1900 TO ENTRY
STA (CVARS),Y ;SET LOW BYTE OF YEAR
LDA CEN20+1
ADC #0
INY
STA (CVARS),Y ;SET HIGH BYTE OF YEAR
;GET HOUR
JSR NXTNUM
LDY #OHR
STA (CVARS),Y
;GET MINUTES
JSR NXTNUM
LDY #OMIN
STA (CVARS),Y
;GET SECONDS
JSR NXTNUM
LDY #OSEC
STA (CVARS),Y
;ENABLE INTERRUPTS
CLI ;ENABLE INTERRUPTS
JSR HOME
;PRINT MONTH
LDY #OMTH
LDA (CVARS),Y
JSR PRTNUM ;PRINT THE NUMBER
LDA
JSR WRCHAR ;PRINT A SLASH
;PRINT DAY
LDY #ODAY
LDA (CVARS),Y
JSR PRTNUM ;PRINT THE NUMBER
LDA
JSR WRCHAR ;PRINT A SLASH
;PRINT YEAR
LDY #OYEAR
LDA (CVARS),Y
SEC
SBC CEN20 ;NORMALIZE YEAR TO 20TH CENTURY
JSR PRTNUM ;PRINT THE NUMBER
;PRINT SPACE AS D
LDA
JSR WRCHAR ;PRINT A SPACE BETWEEN DATE AND TIME
;PRINT HOURS
LDY #OHR
LDA (CVARS),Y
JSR PRTNUM ;PRINT THE NUMBER
LDA #":"
JSR WRCHAR ;PRINT A COLON
;PRINT MINUTES
LDY #OMIN
LDA (CVARS),Y
JSR PRTNUM ;PRINT THE NUMBER
LDA #":"
JSR WRCHAR ;PRINT A COLON
;PRINT SECONDS
LDY #OSEC
LDA (CVARS),Y
JSR PRTNUM ;PRINT THE NUMBER
LDY #OSEC
LDA (CVARS),Y
STA CURSEC ;SAVE IN CURRENT SECOND
WAIT:
;CHECK KEYBOARD
JSR KEYPRS
BCS RDKEY ;BRANCH IF OPERATOR PRESSES A KEY
LDA (CVARS) ,Y ;GET SECONDS
CMP CURSEC
BEQ WAIT ;WAIT UNTIL SECONDS CHANGE
JMP LOOP ;CONTINUE
;OPERATOR PRESSED
RDKEY:
JSR RDCHAR ;GET CHARACTER
CMP #ESC ;IS IT AN ESCAPE?
BEQ DONE ;BRANCH IF IT IS, ROUTINE IS FINISHED
JMP PROMPT ;ELSE PROMPT OPERATOR FOR NEW STARTING TIME
DONE:
LDA #0
STA CH ;CURSOR TO HORIZONTAL POSITION 0
LDA #12
STA CV
JSR VTAB ;MOVE CURSOR TO LINE 13 BELOW DISPLAY
BRK
JMP SC1104 ;CONTINUE AGAIN
********************************
;ROUTINE: KEYPRS
;PURPOSE: DETERMINE IF OPERATOR HAS PRESSED A KEY
;ENTRY: NONE
;EXIT: IF OPERATOR HAS PRESSED A KEY THEN
CARRY = 1
; ELSE
CARRY = 0
;REGISTERS USED: P
KEYPRS:
PHA
LDA 0C000H ;READ APPLE KEYBOARD PORT
ASL A ;MOVE BIT 7 TO CARRY
; CARRY = 1 IF CHARACTER IS READY ELSE 0
PLA
RTS
********************************
;ROUTINE: RDCHAR
;PURPOSE: READ A CHARACTER
;ENTRY: NONE
;EXIT: REGISTER A = CHARACTER
,'REGISTERS USED: A,P
;***************************•***
500 INTERRUPTS
RDCHAR:
PHA ;SAVE A,X,Y
TYA
PHA
TXA
PHA
•A******************************
;ROUTINE: WRCHAR
;PURPOSE: WRITE A CHARACTER
;ENTRY: REGISTER A = CHARACTER
;EXIT: NONE
REGISTERS USED: P
••A*****************************
WRCHAR:
PHA ;SAVE A,X,Y
TYA
PHA
TXA
PHA
TSX
LDA 103HfX ;GET REGISTER A BACK FROM STACK
ORA #10000000B ;SET BIT 7
JSR COUT ;OUTPUT VIA APPLE MONITOR
********************************
•ROUTINE: RDLINE
;PURPOSE: READ A LINE TO 200H USING THE APPLE MONITOR
;ENTRY: NONE
;EXIT: REGISTER X = LENGTH OF LINE
;REGISTERS USED: ALL
.********************************
11D REAL-TIME CLOCK AND CALENDAR (CLOCK) 501
RDLINE:
JSR GETLN1 ;CALL THE APPLE MONITOR GETLNl
RTS
*********************************
•ROUTINE: NXTNUM
;PURPOSE: GET A NUMBER FROM THE INPUT LINE IF ANY
IF NONE RETURN A 0
;ENTRY: LLEN = LENGTH OF THE LINE
; LIDX = INDEX INTO THE LINE OF NEXT CHARACTER
;EXIT: REGISTER A = LOW BYTE OF NUMBER
REGISTER Y = HIGH BYTE OF NUMBER
LIDX = INDEX OF THE FIRST NON NUMERICAL CHARACTER
;REGISTERS USED: ALL
*********************************
NXTNUM:
LDA #0
STA NUM
STA NUM+1 INITIALIZE NUMBER TO 0
;WAIT UNTIL A DECIMAL DIGIT IS FOUND (A CHARACTER BETWEEN 30H AND 39H)
JSR GETCHR ;GET NEXT CHARACTER
BCS EXITNN ;EXIT IF END OF LINE
CMP #"0"
BCC NXTNUM ;WAIT IF LESS THAN "0"
CMP #"9"+l
BCS NXTNUM ;WAIT IF GREATER THAN "9"
;FOUND A NUMBER
GETNUM:
PHA ;SAVE CHARACTER ON STACK
BCC GETNMl
INC NUM+1
GETNMl:
EXITNN:
LDA NUM ;RETURN THE NUMBER
LDY NUM+1
RTS
*********************************
;ROUTINE: GETCHR
;PURPOSE: GET A CHARACTER FOR THE LINE
;ENTRY: LIDX = NEXT CHARACTER TO GET
; LLEN = LENGTH OF LINE
;EXIT: IF NO MORE CHARACTERS THEN
; CARRY = 1
ELSE
CARRY = 0
REGISTER A = CHARACTER
;REGISTERS USED: ALL
********************************
GETCHR:
LDA LIDX
CMP LLEN
BCS EXITGC ;EXIT CHARACTER GET WITH CARRY = 1 TO
; INDICATE END OF LINE (LIDX >= LLEN)
; OTHERWISE, CARRY IS CLEARED
TAY
LDA 200H,Y ;GET CHARACTER
AND tOlllllllB ;CLEAR BIT 7
INY ;INCREMENT TO NEXT CHARACTER
STY LIDX
; CARRY IS STILL CLEARED
EXITGC:
RTS
*******************************
•ROUTINE: PRTNUM
;PURPOSE: PRINT A NUMBER BETWEEN 0..99
;ENTRY: A = NUMBER TO PRINT
;EXIT: NONE
^•REGISTERS USED: ALL
11D REAL-TIME CLOCK AND CALENDAR (CLOCK) 503
PRTNUM :
;.DATA SECTION
CR .EQU ODH ;ASCII CARRIAGE RETURN
MSG .BYTE "ENTER DATE AND TIME ",CR,"(MM/DD/YR HR:MN:SC)? \0
MSGIDX .BLOCK 1 ;INDEX INTO MESSAGE
NUM: .BLOCK 2 ;NUMBER
LLEN: .BLOCK 1 ;LENGTH OF INPUT LINE
LIDX: .BLOCK 1 ;INDEX OF INPUT LINE
CEN20: .WORD 1900 ;20TH CENTURY
CURSEC : .BLOCK 1 ;CURRENT SECOND
.END ;PROGRAM
Appendix A 6502 Instruction Set
Summary
Copyright ® 1982 Synertek, Inc.
Reprinted by permission.
tatlRUCTIORS ■Mtouri AttOlUII IIROPACI ACCUM MPUIO UNO II itaoi. y I.PACI.I AM. X AM.Y RIIATIVI INDIRECT coaoiTioa toon
MNEMONIC OPERATION OP N tt OP N * OP N OP N OP N OP N OP N OP N # OP N 0 OP N OP N • OP N OP N « N Z C I 0 V
61 j J J - - J
ASL 0E 6 3 06 5 2 0A 2 16 6 2 IE 7 3
■ J J J - - -
BIT A AM 2C 4 3 24 3 2 m, y m.
8MI BRANCH ON N-1 (2) 30 2 2
BNE BRANCH ON Z-0 (2) DO 2 2
BPL BRANCH ON N-0 (2) 10 2 2
BRK (SaeFis. A-1) 00 7 1
CLC 0-C 18 2 1
CLD 0-D D8 2 1 0 _
CLI 0-1 58 2 1 0
M
CLV 0-V B8 2 1
CPX X-M EO 2 2 EC 4 3 E4 3 2
CPY Y-M CO 2 2 CC 4 3 C4 3 2
DEC M- 1 -M CE 6 3 C6 5 2 D6 6 2 DE 7 3 y v
DEX X- 1 -X CA 2 1 7 y
DE Y Y- 1 - Y 88 2 1 y y
INX X^ 1 - x E8 2 1 y y
IN Y H-Y C8 2 1 / V
505
506 6502 ASSEMBLY LANGUAGE SUBROUTINES
■Mtaoaic OK RATIO! OP N # OP N # OP N # OP N # OP N # OP N # OP N # OP N # OP N # OP N # OP N # OP N # OP N # N Z C 1 0 V
LDX M-X (1) A2 2 2 AE 4 3 A6 3 2 BE 4 3 B6 4 2 j ,/ _ _ _ _
6
jC 6 2 SE 3 0 V V
7
NOP NO OPERATION EA 2
OR A AVM-A 09 2 2 OD 4 3 05 3 2 01 6 2 11 5 2 15 4 2 1D 4 3 19 4 3 V ,/----
PLA S + 1 -S Ms - A 68 4 1 V V — - - -
ROR 6E 6 3 66 5 2 6A 2 76 6 7E 7
"♦-[cMt 3*^ 1 2 3 J J J - - -
SEO 1-D F8 2 1 _ _ _ _ 1 _
STA A-M 80 4 3 85 3 2 81 6 2 91 6 2 95 4 2 9D 5 3 99 5 3 _ _ _
S T X X -• M RF 4
66 ■j 7
96 2
STY Y — M RC 4 R4 7
94 2
TAX A-X AA 2 1 J V
TAY A-Y A8 2 1 y ^ _ _ _ _
T X S X -• S OA 2
T Y A Y-A 98 2 1 ^ V - - - -
LSD/
BHK
0
1
ORAINO.
2 3 4 5
ORAZ.Ptg*
6
ASL Z. PAGE
7
PHP
8
ORAIMM
9
ASIA
A 8 C D
JSR AND INO. BIT-Z. P*gt AND Z. P«gt ROLZ. P*o* PLP ANOIMM ROL A BIT ABS ANOABS ROL ABS
BMI AND INO, ANOZPm^X ROLZI>k*X sec ANO-ABS.Y ANOABS.X ROL ABS. X
RTI EOR INO. EORZ.P*ge LSRZ.P«gt PHA eonimm LSRA JMPABS eon abs LSRABS
RTS ADC INO. ADCZ.P»9t ROR Z. P*gt PLA ADC IMM ROR A JMPIND ADCABS RORABS
STAINO. > STYZ.Pigt STAZ.P^t STXZ.P.9. DEY TXA STY ABS STA ABS STXA8S
LOY IMM LDAINO. LOXIMM LOYZPio. LOAZ.P«fl« LOX-Z.P«gi TAY LOAIMM TAX LOY ABS LOAABS LDX ABS
acs LOAINO. LOY-Z.P»aX LOA-KPIKX CIV LOA'AM.Y TSK u>v«ass.x* LOA-AW. X LDX-ABS. Y
CPYIMM CMPIND. CPYZ.P»gt CMPZ.Ptg* DECZ.P«gt INV CMPIMM OEX CPYABS CMPABS DEC ABS
CPX-IMM SBC INO. > CPXZ.Pigt SBCZ.PK* INCZ.Pi9t INX SBCIMM NOP CPXABS SBC ABS INCABS
ABS • ABSOLUTE ADDRESSING - THE SECOND BYTE OF THE INSTRUCTION CONTAINS THE 8
LOW ORDER BITS OF THE EFFECTIVE ADDRESS. THE THIRD BYTE CONTAINS THE 8
HIGH ORDER BITS OF THE EFFECTIVE ADDRESS.
Z. PAGE ZERO PAGE ADDRESSING - SECOND BYTE CONTAINS THE 8 LOW ORDER BITS OF
THE EFFECTIVE ADDRESS. THE 8 HIGH ORDER BITS ARE ZERO.
ABS. X ABS. Y ABSOLUTE INDEXED _ THE EFFECTIVE ADDRESS IS FORMED BY ADDING THE
INDEX TO THE SECOND AND THIRD BYTE OF THE INSTRUCTION.
(IND. XI INDEXED INDIRECT - THE SECOND BYTE OF THE INSTRUCTION IS ADOED TO THE X
INDEX. DISCARDING THE CARRY. THE RESULTS POINTS TO A LOCATION ON PAGE
ZERO WHICH CONTAINS THE 8 LOW ORDER BITS OF THE EA. THE NEXT BYTE CON
TAINS THE 8 HIGH ORDER BITS.
(IND). Y • INDIRECT INDEXED - THE SECOND BYTE OF THE INSTRUCTION POINTS TO A LOCA
TION IN PAGE ZERO. THE CONTENTS OF THIS MEMORY LOCATION IS ADDED TO THE Y
INDEX. THE RESULT BEING THE LOW ORDER EIGHT BITS OF THE EA. THE CARRY
FROM THIS OPERATION IS ADDED TO THE CONTENTS OF THE NEXT PAGE ZERO LOCA
TION. THE RESULTS BEING THE 8 HIGH ORDER BITS OF THE EA.
ASSEMBLER DIRECTIVES
LABELS ARE THE FIRST FIELD AND MUST BE FOLLOWED BY AT LEAST ONE SPACE OR A COLON <:)
LABELS CAN BE UP TO 6 ALPHANUMERIC CHARACTERS LONG AND MUST BEGIN WITH AN ALPHA
CHARACTER.
A.X.Y.S.P AND THE 56 OPCODES ARE RESERVED AND CANNOT BE USED AS LABELS.
LABEL " EXPRESSION CAN BE USED TO EQUATE LABELS TO VALUES.
LABEL •«• +N CAN BE USED TO RESERVE AREAS IN MEMORY.
• PC AT TIME OF IRQ OR
NMI THIS INSTRUCTION
WILL COMPLETE BEFORE
INTERRUPT IS SERVICED
• PC AFTER RTI
0 5
0 6
AOL
AOH
AOL
ADH
AOL
IRQ VECTOR
AOH
• HIGH MEMORY
LOW MEMORY
JUMP TO SUBROUTINE
HIGH MEMORY
ACCUMULATOR A
INDEX REGISTER Y
1 X I INDEX REGISTER X
15
7 0
PCH | PCL | PROGRAM COUNTER "PC" OR "P"
L CARRY
ZERO
IRQ DISABLE
1 = CARRY OR NO BORROW
1 = RESULT ZERO
1 = DISABLE
DECIMAL MODE 1 a DECIMAL. 0 » BINARY
BRK COMMAND 1 ALWAYS
NOT USED
OVERFLOW 1 = TRUE
NEGATIVE 1 = NEGATIVE
VSS£ 1 40 2 CA1
PAO [- 2 2 CA2
39
PA1 £ 3 38 RSO
PA2 Q 4 37 RS1
PA3 Q 5 36 3 RS2
PA4 E 6 35
PA5 £ 7 34 3 RES
PA6 E 8 33 Udo
PA7 C 9 32 Ddi
PB1 C 11 30 DD3
PB2 C 12 29 UD4
PB3C 13 28 UD5
PB4 C 14 27 I)D6
PB5 £ 15 26
PB6C 16 25
PB7C 17 24 Dcsi
CB1 C 18 23 IICS2
CB2C 19 22 3 r/w
vcc C 20 21
510
APPENDIX B. PROGRAMMING REFERENCE FOR THE 6522 VIA 511
• TWT
INTERRUPT
CONTROL
INPUT LATCH
FLAGS
(IRA)
(IFR)
OUTPUT BUFFERS
ENABLE
(ORA) (PA)
HER)
DATADIR.
DATA/1 N. DATA
(DDRA)
BUS VV BUS
BUFFERS
PORT A REGISTERS
PERIPHERAL
(PCR)
AUXILIARY
(ACR)
FUNCTION
CONTROL
HANDSHAKE
CONTROL
LATCH LATCH
(TILL)
RES •
(T1LH)
^ SHIFT REG.
• CB1
- CB2
R/W ■> (SR)
COUNTER COUNTER
02 (T1C-H) <T1CL)
<=>
(T2L-U OUTPUT BUFFERS
RS2 ►
(ORB) (PB)
RS3 ■■ COUNTER COUNTER
(T2C-H) (T2C-L)
DATA DIR.
(DDRB)
Figure B-2. Block Diagram of the 6522 Versatile Interface Adapter (VIA)
10 1 0 1 0 SR Shift Register
7 6 5 4 3 2
B • PBO
• PB1
■ PB2
• PB6
. PB7
Pin
Data Direction WRITE READ
Selection
DDRB = "1" (OUTPUT) MPU writes Output Level MPU reads output register bit
(ORB) in ORB. Pin level has no affect.
DDRB = "0" (INPUT) MPU writes into ORB, but MPU reads input level on PB
(Input latching disabled) no effect on pin level, until pin.
DDRB changed.
DDRB = "0" (INPUT) MPU reads IRB bit. which is
(Input latching enabled) the level of the PB pin at the
time of the last CB1 active
transition.
6 5 4 3
B -PAO
• PA1
"PA2
OUTPUT REGISTER "A" (ORA)
-PA3
OR
-PA4
INPUT REGISTER "A" (IRA)
-PAS
-PA6
-PA7,
Pin
Data Direction WRITE READ
Selection
DDRA = "1" (OUTPUT) MPU writes Output Level MPU reads level on PA pin.
(Input latching disabled) (ORA).
DDRA ='1" (OUTPUT) MPU reads IRA bit which is
(Input latching enabled) the level of the PA pin at the
time of the last CA1 active
transition.
DDRA = "0" (INPUT) MPU writes into ORA. but MPU reads level on PA pin.
(Input latching d-sabled) no effect on pin level, until
DDRA changed.
DDRA = "0" (INPUT) MPU reads IRA bit which is
(Input latching enabled) the level of the PA pin at the
time of the last CA1 active
transition.
7 6 5 4 3 2 10
7 6 5 4 3 2 1
-PBO/PAOl -256
-PB1/PA1 -512
- PB2/PA2 -1024
_ PB5/PA5 -8192
. PB6/PA6 -16384
_ PB7/PA7 - 32768
"0" ASSOCIATED PB/PA PIN IS AN INPUT WRITE - 8 BITS LOADED INTO T1 HIGH-ORDER
(HIGH-IMPEDANCE) LATCHES. ALSO, AT THIS TIME BOTH
HIGH AND LOW-ORDER LATCHES
"1" ASSOCIATED PB/PA PIN IS AN OUTPUT,
TRANSFERRED INTO T1 COUNTER.
WHOSE LEVEL IS DETERMINED BY
T1 INTERRUPT FLAG ALSO IS RESET.
ORB/ORA REGISTER BIT.
READ - 8 BITS FROM T1 HIGH-ORDER COUNTER
TRANSFERRED TO MPU.
Figure B-5. Data Direction Registers B Figure B-7. Timer 1 High-Order Counter
(Register 2) and A (Register 3) (Register 5)
7 6 5 4 3 2 1 7 6 5 4 3 2 10
-1
-2
-2
-4
-4
-8
COUNT -8
_ COUNT
VALUE
-16 VALUE
-16
-32
-32
-64
-64
-128
-128
WRITE -8 BITS LOADED INTO T1 LOW-ORDER WRITE - 8 BITS LOADED INTO T1 LOW-ORDER
LATCHES. LATCH CONTENTS ARE LATCHES. THIS OPERATION IS THE
TRANSFERRED INTO LOW-ORDER SAME AS WRITING INTO
COUNTER AT THE TIME THE HIGH- REGISTER 4.
ORDER COUNTER IS LOADED (REG 5).
READ - 8 BITS FROM T1 LOW-ORDER LATCHES
READ - 8 BITS FROM T1 LOW-ORDER COUNTER TRANSFERRED TO MPU. UNLIKE REG 4
TRANSFERRED TO MPU. IN ADDITION, OPERATION, THIS DOES NOT CAUSE
T1 INTERRUPT FLAG IS RESET (BIT 6 RESET OF T1 INTERRUPT FLAG.
IN INTERRUPT FLAG REGISTER).
Figure B-6. Timer 1 Low-Order Counter Figure B-8. Timer 1 Low-Order Latches
(Register 4) (Register 6)
514 6502 ASSEMBLY LANGUAGE SUBROUTINES
7 6 5 4 3 2 10
I i'M'i'n»i
-256
- 256
-512
-512
-1024
- 1024
-2048 _ COUNT
-2048
_ COUNT VALUE
VALUE -4096
-4096
-8192
-8192
- 16384
- 16384
-32768
-32768
WRITE - 8 BITS LOADED INTO T1 HIGH-ORDER WRITE - 8 BITS LOADED INTO T2 HIGH-ORDER
LATCHES. UNLIKE REG 4 OPERATION COUNTER. ALSO, LOW-ORDER LATCHES
NO LATCH-TO-COUNTER TRANSFERS TRANSFERRED TO LOW-ORDER
TAKE PLACE. COUNTER. IN ADDITION. T2 INTERRUPT
READ - 8 BITS FROM T1 HIGH-ORDER LATCHES FLAG IS RESET.
TRANSFERRED TO MPU. READ - 8 BITS FROM T2 HIGHORDER COUNTER
TRANSFERRED TO MPU.
Figure B-9. Timer 1 High-Order Latches Figure B-11. Timer 2 High-Order Counter
(Register 7) (Register 9)
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
-2
-4
-8 COUNT SHIFT
VALUE - REGISTER
-16 BITS
-32
-64
-128
3 2 1
M
■ PA LATCH ENABLE/DISABLE
T1 TIMER CONTROL-
VH 0
SETBY CLEARED BY
TIME
TIME-OUT OF T2 READ T2 LOW OR
R2-
WRITE T2 HIGH
TIME OUT OF T1 READT1 LOW OR
T
MER 1 WRITE T1 HIGH
ANY ENABLED CLEAR ALL
INTERRUPT INTERRUPTS
0 = INTERRUPT DISABLED
1 = INTERRUPT ENABLED
NOTES:
1. IF BIT 7 IS A "0", THEN EACH "1" IN BITS 0 - 6 DISABLES THE
CORRESPONDING INTERRUPT. FOTLJC
2. IF BIT 7 IS A "1", THEN EACH "1" IN BITS 0 - 6 ENABLES THE
CORRESPONDING INTERRUPT.
3 IF A READ OF THIS REGISTER IS DONE. BIT 7 WILL BE "1" AND
ALL OTHER BITS WILL REFLECT THEIR ENABLE/DISABLE STATE.
\ MSD 0 1 2 3 4 5 6 7
LSD \ 000 001 010 011 100 101 110 111
0000 NUL DLE SP 0 @ ~~P %
P
1 0001 SOH DC1 I 1 A Q a q
2 0010 STX DC 2 "
2 B R b r
8 1000 BS CAN ( 8 H X h X
9 1001 HT EM ) 9 I Y i V
A 1010 LF SUB
•
J z j z
B 1011 VT ESC C
+ \ K k }
C 1100 FF FS \
t
< L I 1
D 1101 CR GS - M 3 m {
E 1110 SO RS • > N
A
n
F 1111 SI VS / ?
0 0 DEL
517
Glossary
Accumulator. A register that is the implied source of one operand and the destina
tion of the result for most arithmetic and logic operations.
Active transition (in a PIA or VIA). The edge on the control line that sets an Inter
rupt flag. The alternatives are a negative edge (1 to 0 transition) or a positive
edge (0 to 1 transition).
Address. The identification code that distinguishes one memory location or input/
output port from another and that can be used to select a specific one.
Addressing modes. The methods for specifying the addresses to be used in execut
ing an instruction. Common addressing modes are direct, immediate, indexed,
indirect, and relative.
519
520 6502 ASSEMBLY LANGUAGE SUBROUTINES
Address space. The total range of addresses to which a particular computer may
refer.
Arithmetic shift A shift operation that preserves the value of the sign bit (most
significant bit). In a right shift, this results in the sign bit being copied into the
succeeding bit positions (called sign extension).
Automatic mode (of a peripheral chip). An operating mode in which the peripheral
chip produces control signals automatically without specific program interven
tion.
Base address. The address in memory at which an array or table starts. Also called
starting address or base.
GLOSSARY 521
Baud. A measure of the rate at which serial data is transmitted, bits per second,
but including both data bits and bits used for synchronization, error checking,
and other purposes. Common baud rates are 110, 300, 1200, 2400, 4800, and
9600.
Baud rate generator. A device that generates the proper time intervals between
bits for serial data transmission.
Binary search. A search in which the set of items to be searched is divided into two
equal (or nearly equal) parts during each iteration. The part containing the
item being sought is then determined and used as the set in the next iteration.
A binary search thus halves the size of the set being searched with each itera
tion. This method obviously assumes the set of items is ordered.
Block comparison (or block compare). A search that extends through a block of
memory until either the item being sought is found or the entire block is
examined.
Block move. Moving an entire set of data from one area of memory to another.
Boolean variable. A variable that has only two possible values, which may be
represented as true and false or as 1 and 0. See also Flag.
Bounce. To move back and forth between states before reaching a final state.
Branch instruction. See Jump instruction.
Bubble sort A sorting technique which goes through an array exchanging each
pair of elements that are out of order.
Buffer. Temporary storage area generally used to hold data before it is transferred
to its final destination.
Buffer empty. A signal that is active when any data entered into a buffer or register
has been transferred to its final destination.
Buffer pointer. A storage location that contains the next available address in a
buffer.
Byte. A unit of eight bits. May be described as consisting of a high nibble or digit
(the four most significant bits) and a low nibble or digit (the four least signifi
cant bits).
Call (a subroutine). Transfers control to the subroutine while retaining the infor
mation required to resume the current program. A call differs from a jump or
branch in that a call retains information concerning its origin, whereas a jump
or branch does not.
Carry. A bit that is 1 if an addition overflows into the succeeding digit position.
Carryflag. A flag that is 1 if the last operation generated a carry from the most sig
nificant bit and 0 if it did not.
Centralprocessing unit (CPU). The control section of the computer which controls
its operations, fetches and executes instructions, and performs arithmetic and
logical functions.
Cleaning the stack. Removing unwanted items from the stack, usually by adjust
ing the stack pointer.
Close (a file). To make a file inactive. The final contents of the file are the last
information the user stored in it. The user must generally close a file after
working with it.
Control signal. A signal that directs an I/O transfer or changes the operating mode
of a peripheral.
Data accepted. A signal that is active when the most recent data has been trans
ferred successfully.
Data direction register. A register that determines whether bidirectional I/O lines
are being used as inputs or outputs. Abbreviated as DDR in some diagrams.
Data-link control. A set of conventions governing the format and timing of data
exchange between communicating systems. Also called a protocol.
Data ready. A signal that is active when new data is available to the receiver. Same
as valid data.
Data register. In a PIA or VIA, the actual input/output port. Also called an output
register or a peripheral register.
Debounce. Convert the output from a contact with bounce into a single, clean
transition between states. Debouncing is most commonly applied to outputs
from mechanical keys or switches which bounce back and forth before settling
into their final positions.
Debugger. A program that helps in locating and correcting errors in a user pro
gram. Some versions are referred to as dynamic debugging tools or DDT after
the famous insecticide.
Device address. The address of a port associated with an input or output device.
Diagnostic. A program that checks the operation of a device and reports its find
ings.
Digit shift. A shift of one BCD digit position or four bit positions.
Editor, A program that manipulates text material and allows the user to make cor
rections, additions, deletions, and other changes.
Effective address. The actual address used by an instruction to fetch or store data.
Error-correcting code. A code that the receiver can use to correct errors in
messages; the code itself does not contain any additional message.
Error-detecting code, A code that the receiver can use to detect errors in messages;
the code itself does not contain any additional message.
Even parity, A 1-bit error-detecting code that makes the total number of 1 bits in a
unit of data (including the parity bit) even.
526 6502 ASSEMBLY LANGUAGE SUBROUTINES
External reference. The use in a program of a name that is defined in another pro
gram.
Fill. Placing values in storage areas not previously in use, initializing memory or
storage.
Flag (or condition code or status bit). A single bit that indicates a condition within
the computer, often used to choose between alternative instruction sequences.
Flag (software). An indicator that is either on (1) or off (0) and can be used to
select between two alternative courses of action. Boolean variable and
semaphore are other terms with the same meaning.
Free-running mode. An operating mode for a timer in which it indicates the end of
a time interval and then starts another of the same length. Also referred to as a
continuous mode.
Function key. A key that causes a system to perform a function (such as clearing
the screen of a video terminal) or execute a procedure.
Global. This is a universal variable. Defined in more than one section of a com
puter program, rather than used only locally.
Hardware stack. A stack that the computer automatically manages when execut
ing instructions that use it.
Head (of a queue). The location of the item most recently entered into the queue.
Hexadecimal (or hex). Number system with base 16. The digits are the decimal
numbers 0 through 9, followed by the letters A through F.
Independent mode (of a parallel interface). An operating mode in which the status
and control signals associated with a parallel I/O port can be used indepen
dently of data transfers through the port.
Indirect addressing. An addressing mode in which the effective address is the con
tents of the address included in the instruction, rather than the address itself.
Input/output control block (IOCB). A group of storage locations that contain the
information required to control the operation of an I/O device. Typically
included in the information are the addresses of routines that perform opera
tions such as transferring a single unit of data or determining device status.
Input/output control system (IOCS). A set of computer routines that control the
performance of I/O operations.
Instruction. A group of bits that defines a computer operation and is part of the
instruction set.
Instruction execution time. The time required to fetch, decode, and execute an
instruction.
Interrupt flag. A bit in the input/output section that is set when an event occurs
that requires servicing by the CPU. Typical events include an active transition
on a control line and the exhaustion of a count by a timer.
Interrupt mask (or interrupt enable). A bit that determines whether interrupts will
be recognized. A mask or disable bit must be cleared to allow interrupts,
whereas an enable bit must be set.
Interrupt service routine. A program that performs the actions required to respond
to an interrupt.
GLOSSARY 529
I/O device table. A table that establishes the correspondence between the logical
devices to which programs refer and the physical devices that are actually used
in data transfers. An I/O device table must be placed in memory in order to run
a program that refers to logical devices on a computer with a particular set of
actual (physical) devices. The I/O device table may, for example, contain the
starting addresses of the I/O drivers that handle the various devices.
I/O driver. A computer program that transfers data to or from an I/O device, also
called a driver or I/O utility. The driver must perform initialization functions
and handle status and control, as well as physically transfer the actual data.
Jump instruction (or Branch instruction). An instruction that places a new value in
the program counter, thus departing from the normal one-step incrementing.
Jump instructions may be conditional; that is, the new value may be placed in
the program counter only if a condition holds.
Latch. A device that retains its contents until new data is specifically entered into
it.
Leading edge (of a binary pulse). The edge that marks the beginning of a pulse.
Least significant bit. The rightmost bit in a group of bits, that is, bit 0 of a byte or a
16-bit word.
Linked list A list in which each item contains a pointer (or link) to the next item.
Also called a chain or chained list.
Logical device. The input or output device to which a program refers. The actual
or physical device is determined by looking up the logical device in an I/O
device table — a table containing actual I/O addresses (or starting addresses
for I/O drivers) corresponding to the logical device numbers.
Logical shift. A shift operation that moves zeros in at the end as the original data is
shifted.
Logical sum. A binary sum with no carries between bit positions. See also
Checksum, EXCLUSIVE OR function.
Lookup table. An array of data organized so that the answer to a problem may be
determined merely by selecting the correct entry (without any calculations).
Machine language. The programming language that the computer can execute
directly with no translation other than numeric conversions.
Maintenance (of programs). Updating and correcting computer programs that are
in use.
Majority logic. A combinational logic function that is true when more than half the
inputs are true.
Manual mode (of a peripheral chip). An operating mode in which the chip pro
duces control signals only when specifically directed to do so by a program.
Mask. A bit pattern that isolates one or more bits from a group of bits.
GLOSSARY 531
Mnemonic. A memory jogger, a name that suggests the actual meaning or purpose
of the object to which it refers.
Monitor. A program that allows the computer user to enter programs and data,
run programs, examine the contents of the computer's memory and registers,
and utilize the computer's peripherals. See also Operating system.
Most significant bit. The leftmost bit in a group of bits, that is, bit 7 of a byte or bit
15 of a 16-bit word.
Multifunction device. A device that performs more than one function in a com
puter system; the term commonly refers to devices containing memory, input/
output ports, timers, etc., such as the 6530, 6531, and 6532 devices.
Multitasking. Used to execute many tasks during a single period of time, usually
by working on each one for a specified part of the period and suspending tasks
that must wait for input, output, the completion of other tasks, or external
events.
Murphy's Law. The famous maxim that "whatever can go wrong, will."
Nibble (or nybble). A unit of four bits. A byte (eight bits) may be described as
consisting of a high nibble (four most significant bits) and a low nibble (four
least significant bits).
Nonvolatile memory. A memory that retains its contents when power is removed.
No-op (or no operation). An instruction that does nothing other than increment
the program counter.
Object code (or object program). The program that is the output of a translator
program, such as an assembler. Usually it is a machine language program ready
for execution.
Odd parity. A 1-bit error-detecting code that makes the total number of 1 bits in a
unit of data (including the parity bit) odd.
Open (a file). Make a file ready for use. The user generally must open a file before
working with it.
Operating system (OS). A computer program that controls the overall operations
of a computer and performs such functions as assigning places in memory to
GLOSSARY 533
Operation code (op code). The part of an instruction that specifies the operation to
be performed.
Output register. In a PIA or VIA, the actual input/output port. Also called a data
register or a peripheral register.
P register. See Processor status register, Program counter. Most 6502 reference
material abbreviates program counter as PC and processor status register as P,
but some refer to the program counter as P and the processor status (flag)
register as F.
Packed decimal. A binary-coded decimal format in which each 8-bit byte contains
two decimal digits.
Parallel interface. An interface between a CPU and input or output devices that
handle data in parallel (more than one bit at a time).
Parity. A 1-bit error-detecting code that makes the total number of 1 bits in a unit
of data, including the parity bit, odd (odd parity) or even (even parity). Also
called vertical parity or vertical redundancy check (VRC).
Peripheral Interface. One of the 6500 family versions of a parallel interface; exam
ples are the 6520, 6522, 6530, and 6532 devices.
Peripheral ready. A signal that is active when a peripheral can accept more data.
Peripheral register. In a PIA or VIA, the actual input or output port. Also called a
data register or an output register.
PIA. (Peripheral Interface Adapter). The common name for the 6520 or 6820
device which consists of two bidirectional 8-bit I/O ports, two status lines, and
two bidirectional status or control lines. The 6821 is a similar device.
Pointer. A storage place that contains the address of a data item rather than the
item itself. A pointer tells where the item is located.
Polling. Determining which I/O devices are ready by examining the status of one
device at a time.
Power fail interrupt. An interrupt that informs the CPU of an impending loss of
power.
Priority interrupt system. An interrupt system in which some interrupts have prece
dence over others, that is, they will be serviced first or cam interrupt the
others' service routines.
Processor status (P or F) register. A register that defines the current state of a com
puter, often containing various bits indicating internal conditions. Other
names for this register include condition code register, flag (F) register, status
register, and status word.
Program counter (PC or P register). A register that contains the address of the
next instruction to be fetched from memory.
Programmable I/O device. An I/O device that can have its mode of operation
determined by loading registers under program control.
Programmable peripheral chip. A chip that can operate in a variety of modes; its
current operating mode is determined by loading control registers under pro
gram control.
Programmable timer. A device that can handle a variety of timing tasks, including
the generation of delays, under program control.
Queue. A set of tasks, storage addresses, or other items that are used in a first-in,
first-out manner; that is, the first item entered in the queue is the first to be
removed.
Queue header. A set of storage locations describing the current location and status
of a queue.
536 6502 ASSEMBLY LANGUAGE SUBROUTINES
Random-access memory (RAM). A memory that can be both read and altered
(written) in normal operation.
Read-only memory (ROM). A memory that can be read but not altered in normal
operation.
Ready for data. A signal that is active when the receiver can accept more data.
Real-time operating system. An operating system that can act as a supervisor for
programs that have real-time requirements. May also be referred to as a real
time executive or real-time monitor.
Relative offset. The difference between the actual address to be used in an instruc
tion and the current value of the program counter.
Relocatable. Can be placed anywhere in memory without changes; that is, a pro
gram that can occupy any set of consecutive memory addresses.
Return (from a subroutine). Transfers control back to the program that originally
called the subroutine and resumes its execution.
Rotate. A shift operation that treats the data as if it were arranged in a circle, that
is, as if the most significant and least significant bits were connected either
directly or through a Carry bit.
RS-232 (or EIA RS-232). A standard interface for the transmission of serial
digital data, sponsored by the Electronic Industries Association of Washing
ton, D.C. It has been partially superseded by RS-449.
Scheduler. A program that determines when other programs should be started and
terminated.
Scratchpad. An area of memory that is especially easy and quick to use for storing
variable data or intermediate results. Page 0 is generally used as a scratchpad in
6502-based computers.
SDLC (Synchronous Data Link Control). The successor protocol to BSC for IBM
computers and terminals.
Serial interface. An interface between a CPU and input or output devices that han
dle data serially. Serial interfaces commonly used in 6502-based computers are
the 6551 and 6850 devices. See also UART.
Shift instruction. An instruction that moves all the bits of the data by a certain
number of bit positions, just as in a shift register.
Signed number. A number in which one or more bits represent whether the num
ber is positive or negative. A common format is for the most significant bit to
represent the sign (0 = positive, 1 = negative).
Sign extension. The process of copying the sign (most significant) bit to the right
as in an arithmetic shift. Sign extension preserves the sign when two's comple
ment numbers are being divided or normalized.
Sign flag. A flag that contains the most significant bit of the result of the previous
operation. It is sometimes called a negative flag, since a value of 1 indicates a
negative signed number.
Sign function. A function that is 0 if its parameter is positive and 1 if its parameter
is negative.
Software delay. A program that has no function other than to waste time.
Software interrupt. See Trap.
538 6502 ASSEMBLY LANGUAGE SUBROUTINES
Stack. A section of memory that can be accessed only in a last-in, first-out man
ner. That is, data can be added to or removed from the stack only through its
top; new data is placed above the old data and the removal of a data item makes
the item below it the new top.
Stack pointer. A register that contains the address of the top of a stack.The 6502's
stack pointer contains the address on page 1 of the next available (empty)
stack location.
Standard (or 8,4,2,1) BCD. A BCD representation in which the bit positions have
the same weights as in ordinary binary numbers.
Start bit. A 1-bit signal that indicates the start of data transmission by an
asynchronous device.
Static allocation (of memory). Assignment of fixed storage areas for data and pro
grams, as opposed to dynamic allocation in which storage areas are assigned at
the time when they are needed.
Status register. A register whose contents indicate the current state or operating
mode of a device. See also Processor status register.
Status signal. A signal that describes the current state of a transfer or the operating
mode of a device.
Stop bit. A 1-bit signal that indicates the end of data transmission by an
asynchronous device.
Strobe. A signal that identifies or describes another set of signals and that can be
used to control a buffer, latch, or register.
GLOSSARY 539
Subroutine. A subprogram that can be executed (called) from more than one
place in a main program.
Subroutine call. The process whereby a computer transfers control from its current
program to a subroutine while retaining the information required to resume
the current program.
Suspend (a task). Halts execution and preserves the status of the task until some
future time.
Tail (of a queue). The location of the oldest item in the queue, that is, the earliest
entry.
Task. A self-contained program that can serve as part of an overall system under
the control of a supervisor.
Task status. The set of parameters that specify the current state of a task. A task
can be suspended and resumed as long as its status is saved and restored.
Ten's complement. The result of subtracting a decimal number from zero (ignoring
the negative sign), the nine's complement plus one.
Terminator. A data item that has no function other than to signify the end of an
array.
Top of the stack. The address containing the item most recently entered into the
stack.
Trace. A debugging aid that provides information about a program while the pro
gram is being executed. The trace usually prints all or some of the intermediate
results.
Trailing edge (of a binary pulse). The edge that masks the end of a pulse.
Translate instruction. An instruction that converts its operand into the corres
ponding entry in a table.
Transparent routine. A routine that operates without interfering with the opera
tions of other routines.
Trap (or software interrupt). An instruction that forces a jump to a specific (CPU-
dependent) address, often used to produce breakpoints or to indicate hardware
or software errors.
Two's complement. A binary number that, when added to the original number in a
binary adder, produces a zero result. The two's complement of a number may
be obtained by subtracting the number from zero or by adding 1 to the one's
complement.
Underflow (of a stack). Attempting to remove more data from a stack than has
been entered into it.
Unsigned number. A number in which all the bits are used to represent magnitude.
Valid data. A signal that is active when new data is available to the receiver.
Versatile Interface Adapter (VIA). The name commonly given to the 6522 parallel
interface device; it consists of two 8-bit bidirectional I/O ports, four status and
control lines, two 16-bit timers, and a shift register.
Volatile memory. A memory that loses its contents when power is removed.
Walking bit test. A procedure whereby a single 1 bit is moved through each bit
position in an area of memory and a check is made as to whether it can be read
back correctly.
Word. The basic grouping of bits that a computer can process at one time. In deal
ing with microprocessors, the term often refers to a 16-bit unit of data.
Word boundary. A boundary between 16-bit storage units containing two bytes of
information. If information is being stored in word-length units, only pairs of
bytes conforming to (aligned with) word boundaries contain valid information.
Misaligned pairs of bytes contain one byte from one word and one byte from
another.
Write-only register. A register that the CPU can change but cannot read. If a pro
gram must determine the contents of such a register, it must save a copy of the
data placed there.
Zero flag. A flag that is 1 if the last operation produced a result of zero and 0 if it
did not.
542 6502 ASSEMBLY LANGUAGE SUBROUTINES
Zero page. In 6502 terminology, the lowest 256 memory addresses (addresses
0000 through 00FF).
Zoned decimal. A binary-coded decimal format in which each 8-bit byte contains
only one decimal digit.
Index
AND, 88-89
543
544 6502 ASSEMBLY LANGUAGE SUBROUTINES
u
U ART. See 6551 ACIA; 6850 ACIA
Unconditional branch instructions, 102-03
Underflow of stack, 43, 85
Upside-down addresses, 5 Z flag. See Zero flag
Z-80 microprocessor, differences from 6502, 3, 5,135
Zero flag
branches, 26—27
V (Overflow) flag, 22, 24-25, 27,122,136,138,139 CMP, 22-23,136
Variable base addresses, 32-33 decimal mode, 3
INC, 29,137
inversion in masking, 21, 89
W load instructions, 3,22
Wait instructions, 121-22 masking, 21
Word alignment, 141 meaning, 136
Word boundary, 141 position in status register, vii, 509
.WORD pseudo-operation, viii, 45 transfer instructions, 3, 22
Wraparound on page 0, vii, 52,130 uses of, 21, 26—27
Write-only ports, 49-53,152,153,155 Zero page, special features, 6
Zero page addressing modes
direct, 7,10-11,14
indexed, 8,11-12
X register. See Index registers instructions, 7
If you want to use a specific assembly lan allows you to make instant use of 6502 as
guage routine, learn assembly language sembly language. You can use these sub
quickly, or improve your programme ig skills, routines to
6502 ASSEMBLY LANGUAGE SUBROU
• Run a specific routine.
TINES is for you. It provides code for more
• Speed up a BASIC program.
than 40 common 6502 subroutines, includ
• Assist in programming an I/O driver, a
ing code conversion, array manipulation,
diagnostic, a utility, or a systems
arithmetic, bit manipulation, string pro
program.
cessing, input/output, and interrupts. It
• Quickly learn 6502 assembly lan
describes general 6502 programming
guage programming (based on your
methods (including a quick summary for
knowledge of another micropro
experienced programmers), and tells how
cessor).
to add instructions and addressing modes.
• Improve your programming skills by
It even discusses common 6502 assembly
seeing examples of working routines
language programming errors.
and the shortcuts used.
This book identifies strengths and weak • Debug, maintain, or revise an existing
nesses of the 6502 instruction set, and program.
ISBN 0-931988-59-4