CPU Instruction Reference

Last updated:

Skip this grid if it's your first time.

00
BRK
01
ORA
IzX
02
KIL
03
SLO
IzX
04
NOP
ZP
05
ORA
ZP
06
ASL
ZP
07
SLO
ZP
08
PHP
09
ORA
Imm
0A
ASL
A
0B
ANC
Imm
0C
NOP
Abs
0D
ORA
Abs
0E
ASL
Abs
0F
SLO
Abs
10
BPL
Rel
11
ORA
IzY
12
KIL
13
SLO
IzY
14
NOP
ZPX
15
ORA
ZPX
16
ASL
ZPX
17
SLO
ZPX
18
CLC
Imp
19
ORA
AbsY
1A
NOP
Imp
1B
SLO
AbsY
1C
NOP
AbsX
1D
ORA
AbsX
1E
ASL
AbsX
1F
SLO
AbsX
20
JSR
21
AND
IzX
22
KIL
23
RLA
IzX
24
BIT
ZP
25
AND
ZP
26
ROL
ZP
27
RLA
ZP
28
PLP
29
AND
Imm
2A
ROL
A
2B
ANC
Imm
2C
BIT
Abs
2D
AND
Abs
2E
ROL
Abs
2F
RLA
Abs
30
BMI
Rel
31
AND
IzY
32
KIL
33
RLA
IzY
34
NOP
ZPX
35
AND
ZPX
36
ROL
ZPX
37
RLA
ZPX
38
SEC
Imp
39
AND
AbsY
3A
NOP
Imp
3B
RLA
AbsY
3C
NOP
AbsX
3D
AND
AbsX
3E
ROL
AbsX
3F
RLA
AbsX
40
RTI
41
EOR
IzX
42
KIL
43
SRE
IzX
44
NOP
ZP
45
EOR
ZP
46
LSR
ZP
47
SRE
ZP
48
PHA
49
EOR
Imm
4A
LSR
A
4B
ALR
Imm
4C
JMP
Abs
4D
EOR
Abs
4E
LSR
Abs
4F
SRE
Abs
50
BVC
Rel
51
EOR
IzY
52
KIL
53
SRE
IzY
54
NOP
ZPX
55
EOR
ZPX
56
LSR
ZPX
57
SRE
ZPX
58
CLI
Imp
59
EOR
AbsY
5A
NOP
Imp
5B
SRE
AbsY
5C
NOP
AbsX
5D
EOR
AbsX
5E
LSR
AbsX
5F
SRE
AbsX
60
RTS
61
ADC
IzX
62
KIL
63
RRA
IzX
64
NOP
ZP
65
ADC
ZP
66
ROR
ZP
67
RRA
ZP
68
PLA
69
ADC
Imm
6A
ROR
A
6B
ARR
Imm
6C
JMP
Ind
6D
ADC
Abs
6E
ROR
Abs
6F
RRA
Abs
70
BVS
Rel
71
ADC
IzY
72
KIL
73
RRA
IzY
74
NOP
ZPX
75
ADC
ZPX
76
ROR
ZPX
77
RRA
ZPX
78
SEI
Imp
79
ADC
AbsY
7A
NOP
Imp
7B
RRA
AbsY
7C
NOP
AbsX
7D
ADC
AbsX
7E
ROR
AbsX
7F
RRA
AbsX
80
NOP
Imm
81
STA
IzX
82
NOP
Imm
83
SAX
IzX
84
STY
ZP
85
STA
ZP
86
STX
ZP
87
SAX
ZP
88
DEY
Imp
89
NOP
Imm
8A
TXA
Imp
8B
XAA
Imm
8C
STY
Abs
8D
STA
Abs
8E
STX
Abs
8F
SAX
Abs
90
BCC
Rel
91
STA
IzY
92
KIL
93
SHA
IzY
94
STY
ZPX
95
STA
ZPX
96
STX
ZPY
97
SAX
ZPY
98
TYA
Imp
99
STA
AbsY
9A
TXS
Imp
9B
TAS
AbsY
9C
SHY
AbsX
9D
STA
AbsX
9E
SHX
AbsY
9F
SHA
AbsY
A0
LDY
Imm
A1
LDA
IzX
A2
LDX
Imm
A3
LAX
IzX
A4
LDY
ZP
A5
LDA
ZP
A6
LDX
ZP
A7
LAX
ZP
A8
TAY
Imp
A9
LDA
Imm
AA
TAX
Imp
AB
LAX
Imm
AC
LDY
Abs
AD
LDA
Abs
AE
LDX
Abs
AF
LAX
Abs
B0
BCS
Rel
B1
LDA
IzY
B2
KIL
B3
LAX
IzY
B4
LDY
ZPX
B5
LDA
ZPX
B6
LDX
ZPY
B7
LAX
ZPY
B8
CLV
Imp
B9
LDA
AbsY
BA
TSX
Imp
BB
LAS
AbsY
BC
LDY
AbsX
BD
LDA
AbsX
BE
LDX
AbsY
BF
LAX
AbsY
C0
CPY
Imm
C1
CMP
IzX
C2
NOP
Imm
C3
DCP
IzX
C4
CPY
ZP
C5
CMP
ZP
C6
DEC
ZP
C7
DCP
ZP
C8
INY
Imp
C9
CMP
Imm
CA
DEX
Imp
CB
AXS
Imm
CC
CPY
Abs
CD
CMP
Abs
CE
DEC
Abs
CF
DCP
Abs
D0
BNE
Rel
D1
CMP
IzY
D2
KIL
D3
DCP
IzY
D4
NOP
ZPX
D5
CMP
ZPX
D6
DEC
ZPX
D7
DCP
ZPX
D8
CLD
Imp
D9
CMP
AbsY
DA
NOP
Imp
DB
DCP
AbsY
DC
NOP
AbsX
DD
CMP
AbsX
DE
DEC
AbsX
DF
DCP
AbsX
E0
CPX
Imm
E1
SBC
IzX
E2
NOP
Imm
E3
ISC
IzX
E4
CPX
ZP
E5
SBC
ZP
E6
INC
ZP
E7
ISC
ZP
E8
INX
Imp
E9
SBC
Imm
EA
NOP
Imp
EB
SBC
Imm
EC
CPX
Abs
ED
SBC
Abs
EE
INC
Abs
EF
ISC
Abs
F0
BEQ
Rel
F1
SBC
IzY
F2
KIL
F3
ISC
IzY
F4
NOP
ZPX
F5
SBC
ZPX
F6
INC
ZPX
F7
ISC
ZPX
F8
SED
Imp
F9
SBC
AbsY
FA
NOP
Imp
FB
ISC
AbsY
FC
NOP
AbsX
FD
SBC
AbsX
FE
INC
AbsX
FF
ISC
AbsX

Implied (Imp) — 2 cycles

 2   _ = bus.read(PC); do the op

Accumulator (A) — 2 cycles

 2   _ = bus.read(PC); op on A   // rmw on A, no bus access

Immediate (Imm) — 2 cycles

 2   DL = bus.read(PC); PC++     // DL = operand

Zero Page (ZP) — read/write 3, rmw 5

 2   ADL = bus.read(PC); PC++; ADH = 0

 Access type: read
 3     DL = bus.read(ADH:ADL)
 Access type: write
 3     bus.write(ADH:ADL, reg)
 Access type: rmw
 3     DL = bus.read(ADH:ADL)
 4     bus.write(ADH:ADL, DL)        // dummy: the value just read
 5     bus.write(ADH:ADL, r)    // the modified value

Zero Page,X (ZPX) — read/write 4, rmw 6

 2   ADL = bus.read(PC); PC++; ADH = 0
 3   _ = bus.read(ADH:ADL); ADL = ADL + X    // wraps in ZP

 Access type: read
 4     DL = bus.read(ADH:ADL)
 Access type: write
 4     bus.write(ADH:ADL, reg)
 Access type: rmw
 4     DL = bus.read(ADH:ADL)
 5     bus.write(ADH:ADL, DL)        // dummy
 6     bus.write(ADH:ADL, r)

Zero Page,Y (ZPY) — read/write 4

Same cycles as ZPX with Y, read/write only (no rmw).

Absolute (Abs) — read/write 4, rmw 6

 2   ADL = bus.read(PC); PC++
 3   ADH = bus.read(PC); PC++       // effective address now in ADH:ADL

 Access type: read
 4     DL = bus.read(ADH:ADL)
 Access type: write
 4     bus.write(ADH:ADL, reg)
 Access type: rmw
 4     DL = bus.read(ADH:ADL)
 5     bus.write(ADH:ADL, DL)        // dummy
 6     bus.write(ADH:ADL, r)

Absolute,X (AbsX) — read 4–5, write 5, rmw 7

 2   ADL = bus.read(PC); PC++
 3   ADH = bus.read(PC); PC++; (ADL, CL) = ADL + X
 4   DL = bus.read(ADH:ADL)         // ADH not fixed for page cross yet
     ADH = ADH + CL                 // no-op if CL == 0

 Access type: read
       if (CL == 0): done    // 4 cyc
 5     DL = bus.read(ADH:ADL)    // 5 cyc
 Access type: write
 5     bus.write(ADH:ADL, reg)
 Access type: rmw
 5     DL = bus.read(ADH:ADL)
 6     bus.write(ADH:ADL, DL)        // dummy
 7     bus.write(ADH:ADL, r)

Absolute,Y (AbsY) — read 4–5, write 5, rmw 7

Same cycles as AbsX with Y.

(Indirect,X) (IzX) — read/write 6, rmw 8

 2   ADL = bus.read(PC); PC++; ADH = 0       // ADL = ZP base address
 3   _ = bus.read(ADH:ADL); ADL = ADL + X    // wraps in ZP
 4   IAL = bus.read(ADH:ADL)                 // pointer low, parked
 5   ADL = ADL + 1; ADH = bus.read(ADH:ADL); ADL = IAL   // ADL wraps in ZP

 Access type: read
 6     DL = bus.read(ADH:ADL)
 Access type: write
 6     bus.write(ADH:ADL, reg)
 Access type: rmw
 6     DL = bus.read(ADH:ADL)
 7     bus.write(ADH:ADL, DL)        // dummy
 8     bus.write(ADH:ADL, r)

(Indirect),Y (IzY) — read 5–6, write 6, rmw 8

 2   ADL = bus.read(PC); PC++; ADH = 0       // ADL = ZP address
 3   IAL = bus.read(ADH:ADL)                 // pointer low, parked
 4   ADL = ADL + 1; ADH = bus.read(ADH:ADL); ADL = IAL   // ADL wraps in ZP
 5   (ADL, CL) = ADL + Y
     DL = bus.read(ADH:ADL)         // ADH not fixed for page cross yet
     ADH = ADH + CL                 // no-op if CL == 0

 Access type: read
       if (CL == 0): done    // 5 cyc
 6     DL = bus.read(ADH:ADL)    // 6 cyc
 Access type: write
 6     bus.write(ADH:ADL, reg)
 Access type: rmw
 6     DL = bus.read(ADH:ADL)
 7     bus.write(ADH:ADL, DL)        // dummy
 8     bus.write(ADH:ADL, r)

Relative (Rel) — 2 / 3 / 4 cycles

 2   DL = bus.read(PC); PC++     // DL = signed offset
     if (branch not taken): done     // 2 cyc
 3   NEGATIVE = DL bit 7
     (PCL, CL) = PCL + DL        // treat DL as unsigned for the add
     _ = bus.read(PCH:PCL)
     if (NEGATIVE == CL): done    // 3 cyc
 4   PCH = PCH + (NEGATIVE ? -1 : +1)
     _ = bus.read(PCH:PCL)

Indirect (Ind) — 5 cycles, JMP only

 2   IAL = bus.read(PC); PC++    // pointer low, parked
 3   ADH = bus.read(PC); PC++    // pointer high → ADH
 4   PCL = bus.read(ADH:IAL)     // target low byte
 5   IAL = IAL + 1               // no carry into ADH — bug
     PCH = bus.read(ADH:IAL)     // target high byte

JMP ($02FF) reads the high byte from $0200, not $0300. The increment of IAL at cycle 5 doesn't carry into ADH — classic 6502 bug, not fixed in the 2A03.

Special instructions

These don't decompose into mnemonic + mode; each has its own sequence.

JSR — jump to subroutine

Single opcode 20. 6 cycles.

 2   ADL = bus.read(PC); PC++        // target low parked in ADL
 3   _ = bus.read(01:SP)             // internal operation
 4   bus.write(01:SP, PCH); SP--
 5   bus.write(01:SP, PCL); SP--
 6   PCH = bus.read(PC); PCL = ADL

The address pushed is the PC of JSR's high-operand byte, not the next instruction. RTS pops it and increments by 1, so the round trip lands on the byte right after the JSR.

RTS — return from subroutine

Single opcode 60. 6 cycles.

 2   _ = bus.read(PC)
 3   _ = bus.read(01:SP)
 4   SP++; PCL = bus.read(01:SP)
 5   SP++; PCH = bus.read(01:SP)
 6   PC = PCH:PCL; PC++

BRK — force interrupt

Single opcode 00. 7 cycles. Pushes PC and status, then jumps through the IRQ/BRK vector at $FFFE:$FFFF.

 2   _ = bus.read(PC); PC++          // padding byte: BRK skips the next byte
 3   bus.write(01:SP, PCH); SP--
 4   bus.write(01:SP, PCL); SP--
 5   bus.write(01:SP, P | 0x30); SP--   // status pushed with B and bit 5 set
 6   PCL = bus.read(FFFE); I = 1
 7   PCH = bus.read(FFFF)

The hardware interrupt sequence (NMI/IRQ/RESET) runs these same cycles but suppresses the PC++ and the B flag, and reads a different vector ($FFFA NMI, $FFFE IRQ, $FFFC RESET).

RTI — return from interrupt

Single opcode 40. 6 cycles. Pulls status, then PC — and unlike RTS, no final PC++.

 2   _ = bus.read(PC)
 3   _ = bus.read(01:SP)
 4   SP++; P = bus.read(01:SP)       // B and bit 5 ignored on pull
 5   SP++; PCL = bus.read(01:SP)
 6   SP++; PCH = bus.read(01:SP)

PHA / PHP — push

PHA (48) pushes A; PHP (08) pushes the status byte. 3 cycles.

 2   _ = bus.read(PC)
 3   bus.write(01:SP, src); SP--     // src = A (PHA) or P | 0x30 (PHP)

PHP pushes with B and bit 5 set, same as BRK.

PLA / PLP — pull

PLA (68) pulls into A (sets N, Z); PLP (28) pulls the status byte (B and bit 5 ignored). 4 cycles.

 2   _ = bus.read(PC)
 3   _ = bus.read(01:SP)
 4   SP++; dst = bus.read(01:SP)     // dst = A (PLA) or P (PLP)

KIL — jam

Opcodes 02 12 22 32 42 52 62 72 92 B2 D2 F2 (also called JAM/HLT). These lock the CPU: it stops fetching and hangs until reset. No defined cycle count. You'll never hit one in correct code; a well-behaved emulator can treat it as a fatal error.

Mnemonics

LDA — load accumulator (read)

A := input. Flags: N - - - - - Z -.

LDX — load X (read)

X := input. Flags: N - - - - - Z -.

LDY — load Y (read)

Y := input. Flags: N - - - - - Z -.

STA — store accumulator (write)

output = A.

STX — store X (write)

output = X.

STY — store Y (write)

output = Y.

TAX — transfer A to X

X := A. Flags: N - - - - - Z -.

TAY — transfer A to Y

Y := A. Flags: N - - - - - Z -.

TXA — transfer X to A

A := X. Flags: N - - - - - Z -.

TYA — transfer Y to A

A := Y. Flags: N - - - - - Z -.

TSX — transfer SP to X

X := SP. Flags: N - - - - - Z -.

TXS — transfer X to SP

SP := X. The only transfer that doesn't set N/Z.

ADC — add with carry (read)

A := A + input + C. Flags: N V - - - - Z C. V is set when the signed result doesn't fit (i.e., the result's sign disagrees with the operands'). The 2A03 has decimal mode disabled, so D never affects the result.

SBC — subtract with carry (read)

A := A - input - (1 - C). Flags: N V - - - - Z C. Same V rule as ADC. Opcode EB is an undocumented duplicate of the documented E9.

CMP — compare accumulator (read)

Compares A with input — a subtract that sets flags only. C := A >= input, Z := A == input, N := bit 7 of the difference. Flags: N - - - - - Z C.

CPX — compare X (read)

Compares X with input, like CMP. Flags: N - - - - - Z C.

CPY — compare Y (read)

Compares Y with input, like CMP. Flags: N - - - - - Z C.

AND — logical AND (read)

A := A & input. Flags: N - - - - - Z -.

ORA — logical OR (read)

A := A | input. Flags: N - - - - - Z -.

EOR — logical exclusive OR (read)

A := A ^ input. Flags: N - - - - - Z -.

BIT — bit test (read)

Z := (A & input) == 0, N := input[7], V := input[6]. A is not modified. Flags: N V - - - - Z -.

ASL — arithmetic shift left (rmw)

output = input << 1; C := input[7]. Flags: N - - - - - Z C. Has an Accumulator form (0A).

LSR — logical shift right (rmw)

output = input >> 1; C := input[0]. Flags: N - - - - - Z C. N always ends 0 (bit 7 shifts in as 0). Has an Accumulator form (4A).

ROL — rotate left (rmw)

output = (input << 1) | C; C := input[7]. Flags: N - - - - - Z C. Has an Accumulator form (2A).

ROR — rotate right (rmw)

output = (input >> 1) | (C << 7); C := input[0]. Flags: N - - - - - Z C. Has an Accumulator form (6A).

INC — increment memory (rmw)

output = input + 1. Flags: N - - - - - Z -. C is left untouched even on wrap ($FF → $00); only ADC propagates carry.

DEC — decrement memory (rmw)

output = input - 1. Flags: N - - - - - Z -. C untouched, same as INC.

INX — increment X

X := X + 1. Flags: N - - - - - Z -.

INY — increment Y

Y := Y + 1. Flags: N - - - - - Z -.

DEX — decrement X

X := X - 1. Flags: N - - - - - Z -.

DEY — decrement Y

Y := Y - 1. Flags: N - - - - - Z -.

CLC / SEC — clear / set carry

C := 0 / C := 1. Flags: - - - - - - - C.

CLI / SEI — clear / set interrupt disable

I := 0 / I := 1. Flags: - - - - - I - -.

CLD / SED — clear / set decimal

D := 0 / D := 1. Flags: - - - - D - - -. The 2A03 ignores D in arithmetic, but the flag itself still toggles and can be read back.

CLV — clear overflow

V := 0. Flags: - V - - - - - -. There is no SEV.

BPL / BMI — branch on sign

BPL taken when N == 0, BMI when N == 1. Relative.

BCC / BCS — branch on carry

BCC taken when C == 0, BCS when C == 1. Relative.

BNE / BEQ — branch on zero

BNE taken when Z == 0, BEQ when Z == 1. Relative.

BVC / BVS — branch on overflow

BVC taken when V == 0, BVS when V == 1. Relative.

JMP — unconditional jump

The operand bytes are the destination, loaded straight into PC; there's no read or write at that address. So JMP Abs is 3 cycles (fetch opcode, ADL, ADH→PC), not the 4 of an Abs read — it never touches ADH:ADL. Indirect JMP has the page-boundary bug detailed in the Ind mode section.

NOP — no operation

Does nothing but burn cycles. The documented form is EA (implied, 2 cycles). The undocumented forms come in an implied flavour (1A 3A 5A 7A DA FA, identical to EA) and operand-bearing flavours (immediate, ZP, ZPX, Abs, AbsX) that compute an address and take an input they discard.

SLO — shift left, OR (rmw, illegal)

output = input << 1; C := input[7]; then A := A | output. Flags: N - - - - - Z C. = ASL + ORA.

RLA — rotate left, AND (rmw, illegal)

output = (input << 1) | C; C := input[7]; then A := A & output. Flags: N - - - - - Z C. = ROL + AND.

SRE — shift right, EOR (rmw, illegal)

output = input >> 1; C := input[0]; then A := A ^ output. Flags: N - - - - - Z C. = LSR + EOR.

RRA — rotate right, ADC (rmw, illegal)

output = (input >> 1) | (C << 7); C := input[0]; then A := A + output + C (the new C from the rotate). Flags: N V - - - - Z C. = ROR + ADC.

DCP — decrement, compare (rmw, illegal)

output = input - 1; then compare A - output. Flags: N - - - - - Z C. = DEC + CMP.

ISC — increment, SBC (rmw, illegal)

output = input + 1; then A := A - output - (1 - C). Flags: N V - - - - Z C. = INC + SBC. Also called ISB / INS.

LAX — load A and X (read, illegal)

A := X := input. Flags: N - - - - - Z -. = LDA + LDX. The immediate form (AB) is unstable — see below.

SAX — store A AND X (write, illegal)

output = A & X. Neither A nor X changes.

ANC — AND, copy N to carry (read, illegal)

A := A & input; C := A[7]. Immediate only. Flags: N - - - - - Z C.

ALR — AND, shift right (read, illegal)

A := A & input; then A := A >> 1, C := old A[0]. Immediate only. Flags: N - - - - - Z C. Also called ASR.

ARR — AND, rotate right (read, illegal)

A := A & input; then rotate A right, but V and C come from bits 6 and 5 of the result, not a normal rotate. Immediate only. Flags: N V - - - - Z C. Check a hardware reference if you need the exact V/C.

AXS — (A AND X) minus input (read, illegal)

X := (A & X) - input, a compare-style subtract with no borrow. Immediate only. Flags: N - - - - - Z C. Also called SBX.

XAA — magic AND (read, illegal, unstable)

A := (A | magic) & X & input, where magic is an unpredictable constant. Immediate only. Flags: N - - - - - Z -. Avoid; real code never uses it. Also called ANE.

SHA — store A AND X AND H+1 (write, illegal, unstable)

output = A & X & (ADH + 1). AbsY and IzY. The AND with the high address byte makes it unstable across page crosses. Also called AHX.

SHX — store X AND H+1 (write, illegal, unstable)

output = X & (ADH + 1). AbsY. Same instability as SHA. Also called SXA.

SHY — store Y AND H+1 (write, illegal, unstable)

output = Y & (ADH + 1). AbsX. Same instability as SHA. Also called SYA.

TAS — transfer A AND X to SP, store (write, illegal, unstable)

SP := A & X; then output = SP & (ADH + 1). AbsY. Also called SHS / XAS.

LAS — load A, X, SP from input AND SP (read, illegal)

A := X := SP := input & SP. AbsY. Flags: N - - - - - Z -. Also called LAR.