6502 Assembly
This book is a guide to the 6502 Assembly language. This book will teach the different memory addressing modes and instructions of the 8-bit 6502 processor.
You might want to learn 6502 assembly language programming if you want to do Atari 8 Bit Programming, Commodore 64 Programming, Acorn 8 Bit Programming, NES Programming or Super NES Programming.
Memory Addressing Modes
These are the thirteen memory addressing modes for the 6502 processor including some examples.
Accumulator: A
The Accumulator is implied as the operand, so no address needs to be specified.
Example
Using the ASL (Arithmetic Shift Left) instruction with no operands, the Accumulator is always the value being shifted left.
ASL
Implied: i
The operand is implied, so it does not need to be specified.
Example
The operands being implied here are X, the source of the transfer, and A, the destination of the transfer.
TXA
Immediate: #
The operand is used directly to perform the computation.
Example
The value $22 is loaded into the Accumulator.
LDA #$22
Absolute: a
A full 16-bit address is specified and the byte at that address is used to perform the computation.
Example
The value at address $D010 is loaded into the X register.
LDX $D010
Zero Page: zp
A single byte specifies an address in the first page of memory ($00xx), also known as the zero page, and the byte at that address is used to perform the computation.
Example
The value at address $0002 is loaded into the Y register.
LDY $02
Relative: r
The offset specified is added to the current address stored in the Program Counter (PC). Offsets can range from -128 to +127.
Example
The offset $2D is added to the address in the Program Counter (say $C100). The destination of the branch (if taken) will be $C12D.
BPL $2D
Absolute Indexed with X: a,x
The value in X is added to the specified address for a sum address. The value at the sum address is used to perform the computation.
Example
The value $02 in X is added to $C001 for a sum of $C003. The value $5A at address $C003 is used to perform the add with carry (ADC) operation.
ADC $C001,X
Absolute Indexed with Y: a,y
The value in Y is added to the specified address for a sum address. The value at the sum address is used to perform the computation.
Example
The value $03 in Y is added to $F001 for a sum of $F004. The value $EF at address $F004 is incremented (INC) and $F0 is written back to $F004.
INC $F001,Y
Zero Page Indexed with X: zp,x
The value in X is added to the specified zero page address for a sum address. The value at the sum address is used to perform the computation.
Example
The value $02 in X is added to $01 for a sum of $03. The value $A5 at address $0003 is loaded into the Accumulator.
LDA $01,X
Zero Page Indexed with Y: zp,y
The value in Y is added to the specified zero page address for a sum address. The value at the sum address is used to perform the computation.
Example
The value $03 in Y is added to $01 for a sum of $04. The value $E3 at address $0004 is loaded into the Accumulator.
LDA $01,Y
Zero Page Indexed Indirect: (zp,x)
The value in X is added to the specified zero page address for a sum address. The little-endian address stored at the two-byte pair of sum address (LSB) and sum address plus one (MSB) is loaded and the value at that address is used to perform the computation.
Example
The value $02 in X is added to $15 for a sum of $17. The address $D010 at addresses $0017 and $0018 will be where the value $0F in the accumulator is stored.
STA ($15,X)
Zero Page Indirect Indexed with Y: (zp),y
The value in Y is added to the address at the little-endian address stored at the two-byte pair of the specified address (LSB) and the specified address plus one (MSB). The value at the sum address is used to perform the computation. Indeed addressing mode actually repeats exactly the accumulator register's digits.
Example
The value $03 in Y is added to the address $C235 at addresses $002A and $002B for a sum of $C238. The value $2F at $C238 is shifted right (yielding $17) and written back to $C238.
LSR ($2A),Y
Instructions
These are the instructions for the 6502 processor including an ASCII visual, a list of affected flags, and a table of opcodes for acceptable addressing modes.
Load and Store
Load Accumulator with Memory: LDA
M -> A
Flags: N, Z
Addressing Mode | Opcode |
a | AD |
a,x | BD |
a,y | B9 |
# | A9 |
zp | A5 |
(zp,x) | A1 |
zp,x | B5 |
(zp),y | B1 |
Load Index X with Memory: LDX
M -> X
Flags: N, Z
Addressing Mode | Opcode |
a | AE |
a,y | BE |
# | A2 |
zp | A6 |
zp,y | B6 |
Load Index Y with Memory: LDY
M -> Y
Flags: N, Z
Addressing Mode | Opcode |
a | AC |
a,x | BC |
# | A0 |
zp | A4 |
zp,x | B4 |
Store Accumulator in Memory: STA
A -> M
Flags: none
Addressing Mode | Opcode |
a | 8D |
a,x | 9D |
a,y | 99 |
zp | 85 |
(zp,x) | 81 |
zp,x | 95 |
(zp),y | 91 |
Store Index X in Memory: STX
X -> M
Flags: none
Addressing Mode | Opcode |
a | 8E |
zp | 86 |
zp,y | 96 |
Store Index Y in Memory: STY
Y -> M
Flags: none
Addressing Mode | Opcode |
a | 8C |
zp | 84 |
zp,x | 94 |
Arithmetic
Add Memory to Accumulator with Carry: ADC
A + M + C -> A
Flags: N, V, Z, C
Addressing Mode | Opcode |
a | 6D |
a,x | 7D |
a,y | 79 |
# | 69 |
zp | 65 |
(zp,x) | 61 |
zp,x | 75 |
(zp),y | 71 |
Subtract Memory from Accumulator with Borrow: SBC
A - M - ~C -> A
Flags: N, V, Z, C
Addressing Mode | Opcode |
a | ED |
a,x | FD |
a,y | F9 |
# | E9 |
zp | E5 |
(zp,x) | E1 |
zp,x | F5 |
(zp),y | F1 |
Increment and Decrement
Increment Memory by One: INC
M + 1 -> M
Flags: N, Z
Addressing Mode | Opcode |
a | EE |
a,x | FE |
zp | E6 |
zp,x | F6 |
Increment Index X by One: INX
X + 1 -> X
Flags: N, Z
Addressing Mode | Opcode |
i | E8 |
Increment Index Y by One: INY
Y + 1 -> Y
Flags: N, Z
Addressing Mode | Opcode |
i | C8 |
Decrement Memory by One: DEC
M - 1 -> M
Flags: N, Z
Addressing Mode | Opcode |
a | CE |
a,x | DE |
zp | C6 |
zp,x | D6 |
Decrement Index X by One: DEX
X - 1 -> X
Flags: N, Z
Addressing Mode | Opcode |
i | CA |
Decrement Index Y by One: DEY
Y - 1 -> Y
Flags: N, Z
Addressing Mode | Opcode |
i | 88 |
Shift and Rotate
Arithmetic Shift Left One Bit: ASL
C <- 7 6 5 4 3 2 1 0 <- 0
Flags: N, Z, C
Addressing Mode | Opcode |
a | 0E |
a,x | 1E |
A | 0A |
zp | 06 |
zp,x | 16 |
Logical Shift Right One Bit: LSR
0 -> 7 6 5 4 3 2 1 0 -> C
Flags: N, Z, C
Addressing Mode | Opcode |
a | 4E |
a,x | 5E |
A | 4A |
zp | 46 |
zp,x | 56 |
Rotate Left One Bit: ROL
C <- 7 6 5 4 3 2 1 0 <- C
Flags: N, Z, C
Addressing Mode | Opcode |
a | 2E |
a,x | 3E |
A | 2A |
zp | 26 |
zp,x | 36 |
Rotate Right One Bit: ROR
C -> 7 6 5 4 3 2 1 0 -> C
Flags: N, Z, C
Addressing Mode | Opcode |
a | 6E |
a,x | 7E |
A | 6A |
zp | 66 |
zp,x | 76 |
Logic
AND Memory with Accumulator: AND
A & M -> A
Flags: N, Z
Addressing Mode | Opcode |
a | 2D |
a,x | 3D |
a,y | 39 |
# | 29 |
zp | 25 |
(zp,x) | 21 |
zp,x | 35 |
(zp),y | 31 |
OR Memory with Accumulator: ORA
A | M -> A
Flags: N, Z
Addressing Mode | Opcode |
a | 0D |
a,x | 1D |
a,y | 19 |
# | 09 |
zp | 05 |
(zp,x) | 01 |
zp,x | 15 |
(zp),y | 11 |
Exclusive-OR Memory with Accumulator: EOR
A ^ M -> A
Flags: N, Z
Addressing Mode | Opcode |
a | 4D |
a,x | 5D |
a,y | 59 |
# | 49 |
zp | 45 |
(zp,x) | 41 |
zp,x | 55 |
(zp),y | 51 |
Compare and Test Bit
For all Compare instructions:
Condition | N | Z | C |
Register < Memory | 1 | 0 | 0 |
Register = Memory | 0 | 1 | 1 |
Register > Memory | 0 | 0 | 1 |
Compare Memory and Accumulator: CMP
A - M
Flags: N, Z, C
Addressing Mode | Opcode |
a | CD |
a,x | DD |
a,y | D9 |
# | C9 |
zp | C5 |
(zp,x) | C1 |
zp,x | D5 |
(zp),y | D1 |
Compare Memory and Index X: CPX
X - M
Flags: N, Z, C
Addressing Mode | Opcode |
a | EC |
# | E0 |
zp | E4 |
Compare Memory with Index Y: CPY
Y - M
Flags: N, Z, C
Addressing Mode | Opcode |
a | CC |
# | C0 |
zp | C4 |
Test Bits in Memory with Accumulator: BIT
A & M
Flags: N = M7, V = M6, Z
Addressing Mode | Opcode |
a | 2C |
# | 89 |
zp | 24 |
Branch
Branch on Carry Clear: BCC
Branch if C = 0
Flags: none
Addressing Mode | Opcode |
r | 90 |
Branch on Carry Set: BCS
Branch if C = 1
Flags: none
Addressing Mode | Opcode |
r | B0 |
Branch on Result Zero: BEQ
Branch if Z = 1
Flags: none
Addressing Mode | Opcode |
r | F0 |
Branch on Result Minus: BMI
Branch if N = 1
Flags: none
Addressing Mode | Opcode |
r | 30 |
Branch on Result not Zero: BNE
Branch if Z = 0
Flags: none
Addressing Mode | Opcode |
r | D0 |
Branch on Result Plus: BPL
Branch if N = 0
Flags: none
Addressing Mode | Opcode |
r | 10 |
Branch on Overflow Clear: BVC
Branch if V = 0
Flags: none
Addressing Mode | Opcode |
r | 50 |
Branch on Overflow Set: BVS
Branch if V = 1
Flags: none
Addressing Mode | Opcode |
r | 70 |
Transfer
Transfer Accumulator to Index X: TAX
A -> X
Flags: N, Z
Addressing Mode | Opcode |
i | AA |
Transfer Index X to Accumulator: TXA
X -> A
Flags: N, Z
Addressing Mode | Opcode |
i | 8A |
Transfer Accumulator to Index Y: TAY
A -> Y
Flags: N, Z
Addressing Mode | Opcode |
i | A8 |
Transfer Index Y to Accumulator: TYA
Y -> A
Flags: N, Z
Addressing Mode | Opcode |
i | 98 |
Transfer Stack Pointer to Index X: TSX
S -> X
Flags: N, Z
Addressing Mode | Opcode |
i | BA |
Transfer Index X to Stack Pointer: TXS
X -> S
Flags: none
Addressing Mode | Opcode |
i | 9A |
Stack
Push Accumulator on Stack: PHA
A -> S
Flags: none
Addressing Mode | Opcode |
i | 48 |
Pull Accumulator from Stack: PLA
S -> A
Flags: N, Z
Addressing Mode | Opcode |
i | 68 |
Push Processor Status on Stack: PHP
P -> S
The processor status is stored as a single byte with the following flags bits from high to low: NV-BDIZC.
Flags: none
Addressing Mode | Opcode |
i | 08 |
Pull Processor Status from Stack: PLP
S -> P
Setting the processor status from the stack is the only way to clear the B (Break) flag.
Flags: all
Addressing Mode | Opcode |
i | 28 |
Subroutines and Jump
Jump to New Location: JMP
Jump to new location
Flags: none
Addressing Mode | Opcode |
a | 4C |
(a) | 6C |
Jump to New Location Saving Return Address: JSR
Jump to Subroutine
Flags: none
Addressing Mode | Opcode |
a | 20 |
Return from Subroutine: RTS
Return from Subroutine
Flags: none
Addressing Mode | Opcode |
i | 60 |
Return from Interrupt: RTI
Return from Interrupt
Flags: all
Addressing Mode | Opcode |
i | 40 |
Set and Clear
Set Carry Flag: SEC
1 -> C
Flags: C = 1
Addressing Mode | Opcode |
i | 38 |
Set Decimal Mode: SED
1 -> D
Flags: D = 1
Addressing Mode | Opcode |
i | F8 |
Set Interrupt Disable Status: SEI
1 -> I
Flags: I = 1
Addressing Mode | Opcode |
i | 78 |
Clear Carry Flag: CLC
0 -> C
Flags: C = 0
Addressing Mode | Opcode |
i | 18 |
Clear Decimal Mode: CLD
0 -> D
Flags: D = 0
Addressing Mode | Opcode |
i | D8 |
Clear Interrupt Disable Status: CLI
0 -> I
Flags: I = 0
Addressing Mode | Opcode |
i | 58 |
Clear Overflow Flag: CLV
0 -> V
Flags: V = 0
Addressing Mode | Opcode |
i | B8 |
Miscellaneous
No Operation: NOP
No Operation
Flags: none
Addressing Mode | Opcode |
i | EA |
Break: BRK
Force an Interrupt
Flags: B = 1, I = 1
Addressing Mode | Opcode |
i | 00 |
Further Reading
- Owad, Tom, "Apple I Replica Creation", Syngress, 2005. ISBN 193183640X
- 6502.org the 6502 microprocessor resource, particularly the Tutorials and Primers page.
- NES Programming: The Nintendo Entertainment System uses a version of the 6502
- Super NES Programming: the Super NES uses the 65c816, a descendant of the 6502
- History of Apple Inc.: early Apple computers all used some version of the 6502
- History of Computers/The Rise of the Microcomputer
- X86 Disassembly/Disassemblers and Decompilers#Disassembly of 8 bit CPU code mentions some 6502 disassemblers
- Computer Programming/Hello world#Accumulator .2B index register machine: MOS Technology 6502.2C CBM KERNEL.2C MOS assembler syntax