A WORD ABOUT NUMBERS
Before we can discuss the 6502 instruction set itself, we need to briefly discuss some shorthand used in all 6502 assemblers. This will allow us to write numbers and abbreviations properly, and let us understand one another.
Whenever a number is used in an assembly language instruction, it must be preceded by a number sign, #. For example, if we refer to the number 2, we need to write #2. Then the assembler can distinguish between a number and a specific address inside the computer. When the number is preceded by the # sign, the assembler knows that you mean a number, and when a number appears alone, the understanding is that you mean an address. Take the following examples, written in English, for instance:
add 2 to SUM | SUM+#2 |
add the contents | |
of memory location 2 | |
to SUM | SUM+2 |
The single biggest mistake that beginning assembly language programmers make is to use numbers for addresses and addresses for numbers. This will completely destroy any program, and if you're not familiar with assembly language programming, you can look at a printout of the program for days without spotting the error.
The second convention used in assembly language programming involves number base. Whenever a number appears either alone or preceded only by the # sign, the assembler knows that you mean base 10, the decimal system; for example,
SUM+ #11
The assembler interprets this to mean that the decimal number 11 should be added to the value of SUM. Similarly, we could write this:
SUM+ 11
The assembler interprets this to mean that the contents of memory location 11 (in the decimal numbering system) should be added to the value of SUM.
When we want to use the hexadecimal numbering system, we precede the number with a dollar sign; for example:
SUM+$11
This instruction means to add the value of SUM to the contents of memory location $11 (which is location 17 in the decimal system). Things get somewhat more complicated when we refer to a hexadecimal number. We must first tell the assembler that a number is coming, and then tell it that this number is in the hexadecimal system. Our example now looks like this:
SUM+ #$11
This instruction means to add the hexadecimal number $11 (decimal 17) to the value of SUM. It cannot be misinterpreted by the assembler. Unfortunately, it certainly can be mistakenly written in a wide variety of forms by the novice to assembly language programming. So, to be forewarned is to be forearmed. These types of mistakes in writing assembly language programs are very common, and, if your first programs do not work, you should check for these types of mistakes first, before looking for complicated errors of logic.
A third type of number recognized by most assemblers is rarely used, but when it's needed, you'll be glad it's available. This is the binary system, which is usually prefaced by a percent sign, %; for instance,
#%11010110
There can be no confusion about the interpretation of this number, since the % sign clearly labels it as a binary number. Furthermore, the decimal number 11,010,110 is much too large to be directly addressed by the 6502-based computers.
To review, the # sign identifies the term following it as a number, to distinguish it from an address. The $ sign identifies the term following it as a hexadecimal term, and it follows the # sign, where a hexadecimal number is meant. The % sign identifies the next term as a binary term, and also follows the # sign in the case of a binary number. When neither the $ sign nor the % sign precedes the term, the decimal system is understood.
THE 6502 INSTRUCTION SET
Each instruction in the 6502 instruction set is described in detail in Appendix 1. We will briefly discuss the instructions here to familiarize you with the nomenclature and the use of the instructions. Each instruction is a three-letter abbreviation of the full name of the instruction. This abbreviation is called a mnemonic, and once learned, is fairly easy to remember. We'll cover the way these instructions address memory in Chapter 5.
In this section, we will discuss the instructions in groups, concentrating on how the instructions can be used in programming.
THE LOAD INSTRUCTIONS
There are three instructions in this group:
LDA LoaD the Accumulator
LDX LoaD the X register
LDY LoaD the Y register
These instructions are in some respects similar to the PEEK instruction in BASIC. The PEEK instruction retrieves the value stored in a specific memory location. Any of the LOAD instructions can also be used to retrieve a value from memory, as in the following example:
LDA
$0243
This command takes the value previously stored in the memory location with the address $0243, and places a copy of that value into the accumulator for further manipulation. Note particularly the use of the word copy in this statement. Like the PEEK command in BASIC, the LOAD instructions in assembly language programming do not change the value stored in the location from which the load takes place. Location $0243 contains the same value before and after the LDA instruction is executed; however, the value contained in the accumulator changes as a result of this instruction. We could have chosen to transfer this value from location $0243 to either the X or the Y register; in this case, the above line would have read either LDX $0243 or LDY $0243 respectively.
Since we already know that all calculations such as addition and subtraction are done in the accumulator, one use of the LDA instruction becomes obvious. There are, of course, many other uses for this instruction. The other two LOAD instructions, LDX and LDY, are used to load either of the registers with a specific value, usually prior to using the register in some other operation, such as counting. Many examples of the LOAD instructions will be discussed throughout the book.
THE STORE INSTRUCTIONS
As we discussed, the BASIC PEEK command and the assembly language LOAD instructions are somewhat similar. In assembly language, we also have commands analogous to the BASIC POKE command, the STORE commands. Since there are three LOAD commands, it is not surprising to find that there are also three STORE commands:
STA STore the Accumulator
STX STore the X Register
STY STore the Y Register
STX STore the X Register
STY STore the Y Register
A typical line of assembly language code using these instructions might appear as follows:
STX $0243
This instruction copies whatever value was previously stored in the X register into memory location $0243. The analogy with the BASIC POKE command is obvious. As with the LOAD instructions, the value stored in either the accumulator or the registers, depending on which instruction is used, is not changed by the execution of the instruction. Therefore, if you wanted to store the number 8 into four different memory locations, the following code could be used:
LDX
#8 ;first, load it in
STX $CC ;the first location
STX $CD ;the 2nd location
STX $12 ;the 3rd location
STX $D5 ;and we're done
STX $CC ;the first location
STX $CD ;the 2nd location
STX $12 ;the 3rd location
STX $D5 ;and we're done
Note especially that we don't have to reload the X register with 8 before each store command. The value remains there until we change it. Of course, we could just as easily have used either the accumulator or the Y register to accomplish the above goal in the same fashion.
One very common use of the LOAD and STORE instructions is to transfer the values stored in one or more memory locations into different locations. For example,
LDA
$5982 ;get the 1st value
STA $0243 ;transfer it
LDA $4903 ;get the 2nd
STA $82 ;and so on...
STA $0243 ;transfer it
LDA $4903 ;get the 2nd
STA $82 ;and so on...
In Chapter 7, we'll see how to use this type of routine to write subroutines which can speed up your BASIC programs amazingly.
TRANSFER OF CONTROL INSTRUCTIONS
Two types of instructions cause program control to shift from one place in the program to another. These are the JUMP instructions and the BRANCH instructions.
THE JUMP INSTRUCTIONS
For the purposes of this discussion, we have grouped two instructions into this category:
JMP JuMP to a specific address
JSR Jump to a SubRoutine
JSR Jump to a SubRoutine
These two instructions are analogous to the BASIC commands GOTO and GOSUB, respectively. Both instructions result in unconditional transfer of program flow. Here's an example of the JMP instruction:
JMP SUB1 ;GOTO SUB1
SUB0 LDA #1 ;to inhibit cursor
STA 752 ;store a 1 here
SUB1 LDA #0 ;to reset cursor
STA 752 ;store a 0 here
SUB0 LDA #1 ;to inhibit cursor
STA 752 ;store a 1 here
SUB1 LDA #0 ;to reset cursor
STA 752 ;store a 0 here
In this example, the cursor will never be inhibited, since whenever the program gets to the JMP instruction, the line labeled SUB1 is executed next. This transfer of control is unconditional; that is, it will happen every time. The 2-line routine labeled SUB0 will never get executed.
In contrast, let's look at an example of the JSR instruction:
JSR SUB1 ;GOSUB SUB1
SUB0 LDA #1 ;to inhibit cursor
STA 752 ;store a 1 here
JMP SUB2 ;to avoid SUB1
SUB1 LDA #0 ;to reset cursor
STA 752 ;store a 0 here
RTS ;like BASIC's RETURN
SUB2 ... ;more code...
SUB0 LDA #1 ;to inhibit cursor
STA 752 ;store a 1 here
JMP SUB2 ;to avoid SUB1
SUB1 LDA #0 ;to reset cursor
STA 752 ;store a 0 here
RTS ;like BASIC's RETURN
SUB2 ... ;more code...
In this routine, we JSR to the subroutine labeled SUB1. The program then executes the lines in order, until an RTS (ReTurn from Subroutine) instruction is encountered. Program control then reverts to the line following the JSR that sent control to the subroutine in the first place. The RTS instruction is the assembly language counterpart to the BASIC RETURN command, which also marks the end of a subroutine. In the example, first SUB1 will execute, and then SUB0 will execute. The JMP instruction following SUB0 simply prevents SUB1 from executing a second time. There is another instruction in assembly language which is similar to the RTS instruction, the RTI (ReTurn from Interrupt). This instruction is used at the end of an interrupt routine to return control to the main program, like the RTS instruction. We'll discuss interrupts at great length in later chapters.
THE BRANCH INSTRUCTIONS
In contrast to the two unconditional transfer of control instructions just discussed, the 6502 has an extensive set of conditional transfer of control instructions. These can be compared to the IF…THEN construction of BASIC:
IF
X=5 THEN GOTO 710
This statement will transfer control to line 710 only if X is equal to 5. If it equals any other value, program control will shift to the next line of code following the IF statement. In a sense, by coding this line we have allowed the computer to decide what to do, depending on conditions we have established; and we've set up a conditional transfer of control. These are the branch instructions of the 6502 instruction set:
BCC Branch on Carry Clear
BCS Branch on Carry Set
BEQ Branch on result EQual to zero
BMI Branch on result MInus
BNE Branch on result Not Equal to zero
BPL Branch on result PLus
BVC Branch on oVerflow Clear
BVS Branch on oVerflow Set
BCS Branch on Carry Set
BEQ Branch on result EQual to zero
BMI Branch on result MInus
BNE Branch on result Not Equal to zero
BPL Branch on result PLus
BVC Branch on oVerflow Clear
BVS Branch on oVerflow Set
Each of these instructions depends on the value of one of the flags in the processor status register. Whether or not the branch is taken depends on the value of that flag at that time, so these are clearly conditional transfer of control instructions. Let's look at a simple example to see how these instructions work:
LDA #0 ;initialize
BCC SUB4 ;branch if carry clear
LDA #1 ;if not
SUB4 STA $0243 ;store the value here
BCC SUB4 ;branch if carry clear
LDA #1 ;if not
SUB4 STA $0243 ;store the value here
In this routine, the value stored into memory location $0243 depends on the condition of the carry flag in the processor status register at the time the branch instruction is executed. If the carry flag is set (equal to 1), then the branch is not taken, and the accumulator is loaded with the value 1 before the STA command. If the carry flag is clear (equal to zero), the branch is taken, the accumulator is not changed, and the value 0 is stored into memory location $0243. The BCS instruction is the opposite of the BCC instruction: the branch is taken if the carry bit is set and is not taken if the carry bit is clear.
The BEQ and BNE instructions depend on the value of the zero flag in the processor status register, rather than on the value of the carry flag. If the zero flag is clear, the BEQ branch is not taken, but the BNE branch is taken. If the zero flag is set, the BEQ branch is taken, and the BNE is not. For instance, we do not take the branch here:
LDA
#0 ;sets the zero flag
BNE SUB4 ;branch is not taken
BNE SUB4 ;branch is not taken
but we would have branched to SUB4 if we had written this:
LDA
#1 ;clears the zero flag
BNE SUB4 ;branch is taken
BNE SUB4 ;branch is taken
The overflow flag is used to determine the outcome of the BVC and BVS instructions in an analogous fashion. Similarly, the negative flag determines the outcome of the BMI and BPL instructions. If previous instructions produce a negative answer, then a branch based on the BMI instruction is taken. If this answer is either positive or equal to zero, the BPL instruction is taken. Used appropriately, these eight instructions, which depend on the values of four of the flags in the processor status register, can give extremely fine control over program flow in assembly language programs, as we shall see in subsequent chapters.
PROCESSOR STATUS REGISTER INSTRUCTIONS
These instructions directly manipulate the flags in the processor status register:
CLC CLear the Carry flag
CLD CLear the Decimal flag
CLI CLear the Interrupt flag
CLV CLear the oVerflow flag
SEC SEt the Carry flag
SED SEt the Decimal flag
SEI SEt the Interrupt flag
CLD CLear the Decimal flag
CLI CLear the Interrupt flag
CLV CLear the oVerflow flag
SEC SEt the Carry flag
SED SEt the Decimal flag
SEI SEt the Interrupt flag
These instructions perform the indicated operations directly on the flags of the processor status register, and their operation, which is self-explanatory, is further described in Appendix 1.
ARITHMETIC AND LOGICAL INSTRUCTIONS
We will place all of the calculating instructions of the 6502 into this group of instructions.
ADC ADd with Carry
AND the logical AND instruction
ASL Arithmetic Shift Left
BIT test BITs of memory with the accumulator
EOR Exclusive OR
LSR Logical Shift Right
ORA logically OR memory with the Accumulator
ROL ROtate Left
ROR ROtate Right
SBC SuBtract with Carry
AND the logical AND instruction
ASL Arithmetic Shift Left
BIT test BITs of memory with the accumulator
EOR Exclusive OR
LSR Logical Shift Right
ORA logically OR memory with the Accumulator
ROL ROtate Left
ROR ROtate Right
SBC SuBtract with Carry
These are all complex instructions; for a detailed explanation of them, please see Appendix 1; for a brief discussion, read on.
The ADC instruction is the fundamental addition instruction of the 6502. It takes the sum of the value stored in the accumulator, plus the carry bit in the processor status register, plus the number addressed by the ADC instruction itself. For instance, let's add the contents of memory location $0434 to the contents of memory location $0435, and store the result in memory location $0436:
CLC
;clear carry bit first
LDA $0434 ;get 1st number
ADC $0435 ;add 2nd number
STA $0436 ;store the result
LDA $0434 ;get 1st number
ADC $0435 ;add 2nd number
STA $0436 ;store the result
We'll be using ADC frequently throughout the remainder of this book. Its counterpart is the subtraction instruction, SBC. SBC subtracts the value addressed from the value stored in the accumulator, using the carry bit of the processor status register if a borrow is needed to perform the subtraction. To subtract the same values we added above, we would write this:
SEC
;in case we need to borrow
LDA $0434 ;get 1st number
SBC $0435 ;subtract 2nd one
STA $0436 ;store the result
LDA $0434 ;get 1st number
SBC $0435 ;subtract 2nd one
STA $0436 ;store the result
There are four SHIFT instructions in this group, ASL, LSR, ROL, and ROR. These instructions all shift the bits of a number, but in different ways. The two ROTATE instructions use the carry bit of the processor status register, and literally rotate the 8 bits of the number addressed through the 9 positions (8 in the number itself, and 1 from the carry bit). Pictorially, this looks like the following example:
ROR
$0434 ;rotate right contents of $0434
START
in $0434 in C
10110100 1
END
in $0434 in C
11011010 0
in $0434 in C
10110100 1
END
in $0434 in C
11011010 0
As you can see, each bit rotated one position to the right, with bit 0 ending up in the carry bit and the former carry bit ending up in bit 7 of location $0434.
The ROL instruction simply reverses the rotation, to the left instead of the right. The two SHIFT instructions ASL and LSR work in almost the same way, except that although the end bit winds up in the carry bit as above, zero, instead of whatever was in the carry bit, is always rotated into the number.
These four SHIFT instructions are used to multiply or divide by powers of 2, since by rotating bits to the left, we double a number, and by rotating bits to the right, we effectively divide a number by 2. There are cautions to observe when using these instructions for this purpose, however, as will be described in Appendix 1.
The three logical instructions, AND, EOR, and ORA, are simply three ways of comparing two numbers bit by bit. They take the binary forms of the two numbers being compared, and, depending on whether both numbers contain a one or a zero in each bit, produce different results. The AND instruction says "If both bits are 1, the result will also have a 1 in that position. If not, the result will have a zero in that position." The EOR instruction says "If one, and only one, of the numbers has a 1 in that position, the result will also have a 1 in that position. If both numbers have a 1 or both contain 0, the result will have a zero in that position" Finally, the ORA instruction says "If either or both numbers have a 1 in this position, the result will also have a 1 in this position." These three logical instructions are used in a wide variety of ways. ORA is most commonly used to set a specific bit in a number, EOR to complement a number, and AND to clear a specific bit of a number. If you are unfamiliar with these three logical operations, see Appendix 1 for further details.
The final instruction of this group is BIT, which is a testing instruction. BIT sets the negative flag of the processor status register equal to bit 7 of the number being tested, the overflow flag equal to bit 6 of the number being tested. BIT also sets the zero flag, depending on the result of ANDing the number being tested with that stored in the accumulator. This instruction tests several aspects of a number all at once. Note that the number in the accumulator is unchanged by the BIT instruction. By following this instruction with one of the BRANCH instructions we have already discussed, we can cause an appropriate branch in the execution of the program.
6502 MANIPULATION INSTRUCTIONS
Like the LOAD and STORE instructions we discussed earlier, the following instructions involve interchanging information from one part of the computer to another:
PHA PusH
the Accumulator onto the stack
PHP PusH the Processor status register onto the stack
PLA PuLl from the stack into the Accumulator
PLP PuLl from the stack into the Processor status register
TAX Transfer the Accumulator to the X register
TAY Transfer the Accumulator to the Y register
TSX Transfer the Stack pointer to the X register
TXA Transfer the X register to the Accumulator
TXS Transfer the X register to the Stack pointer
TYA Transfer the Y register to the Accumulator
PHP PusH the Processor status register onto the stack
PLA PuLl from the stack into the Accumulator
PLP PuLl from the stack into the Processor status register
TAX Transfer the Accumulator to the X register
TAY Transfer the Accumulator to the Y register
TSX Transfer the Stack pointer to the X register
TXA Transfer the X register to the Accumulator
TXS Transfer the X register to the Stack pointer
TYA Transfer the Y register to the Accumulator
The functions of these instructions, too, are self-explanatory. They are used to interchange information between the various registers of the 6502, or to store information on the stack for later retrieval. The PHA and PLA instructions are used frequently to pass information between a BASIC program and a machine language subroutine, as we shall see.
INCREMENTING AND DECREMENTING INSTRUCTIONS
Instructions in this group can increase or decrease by 1 the value contained either in a specific memory location, or in one of the 6502 registers:
DEC DECrement a memory location by one
DEX DEcrement the X register by one
DEY DEcrement the Y register by one
INC INCrement a memory location by one
INX INcrement the X register by one
INY INcrement the Y register by one
DEX DEcrement the X register by one
DEY DEcrement the Y register by one
INC INCrement a memory location by one
INX INcrement the X register by one
INY INcrement the Y register by one
These instructions are straightforward. Here is an example of their use:
LDA
#3 ;start with 3
STA $0243 ;stored here
INC $0243 ;now it's a 4
INC $0243 ;now it's a 5
DEC $0243 ;now it's a 4 again
STA $0243 ;stored here
INC $0243 ;now it's a 4
INC $0243 ;now it's a 5
DEC $0243 ;now it's a 4 again
Note that there is no incrementing or decremeting instruction which operates on the accumulator. To increase or decrease a number in the accumulator, we must use the ADC or SBC instruction. Therefore, if a simple increment or decrement is required, it is easier to load a number into either the X or Y register, rather than into the accumulator, and then simply use the appropriate increment or decrement instruction.
THE COMPARE INSTRUCTIONS
Three instructions allow comparisons to be made between two values. These instructions condition various flags in the processor status register, depending upon the outcome of the comparison:
CMP CoMPare
the accumulator with memory
CPX ComPare the X register with memory
CPY ComPare the Y register with memory
CPX ComPare the X register with memory
CPY ComPare the Y register with memory
The way each of these affects the processor status register is described fully in Appendix 1, but a simple example is given here to demonstrate the use of the COMPARE instructions:
LDA
$0243 ;get the 1st number
CMP $0244 ;compare it to the 2nd
BNE SUB6 ;go to SUB6 if $244 >$243
LDA #1 ;else, do this
CMP $0244 ;compare it to the 2nd
BNE SUB6 ;go to SUB6 if $244 >$243
LDA #1 ;else, do this
THE REMAINING INSTRUCTIONS
Two final instructions do not easily fall into any grouping. These are the BRK (BReaK) and the NOP (No OPeration) instructions. The BRK instruction is used primarily in debugging your program once you've written it. It causes the program being executed to stop, and is somewhat similiar in this regard to the BASIC STOP instruction. The NOP instruction does nothing; its primary function is to reserve space in a program for future changes which may need to be made. It may be necessary to reserve this space, since frequently in an assembly language program, the exact memory location occupied by an instruction may be critical, and NOP instructions in the code can be replaced by functional commands without changing the location in memory of the instructions that follow it.
This completes our short introduction to the instruction set of the 6502. As we have already stated, details on any of these instructions can be found in Appendix 1. It is strongly advised that beginners to assembly language read Appendix 1 thoroughly. If you are already familiar with the instruction set, this short discussion should have refreshed the instructions in your mind, and you are ready to proceed.
Return to Table of Contents | Previous Chapter | Next Chapter