6502/6510/8500/8502 Opcode matrix:

imm = #$00

zp = $00

zpx = $00,X

zpy = $00,Y

izx =
($00,X)

izy = ($00),Y

abs = $0000

abx = $0000,X

aby = $0000,Y

ind
= ($0000)

rel = $0000 (PC-relative)

**x0****x1****x2****x3****x4****x5****x6****x7****x8****x9****xA****xB****xC****xD****xE****xF**
**0x****BRK7**

izx 6

izx 8

zp 3

zp 3

zp 5

zp 5

3

imm 2

2

imm 2

abs 4

abs 4

abs 6

abs 6

rel 2*

izy 5*

izy 8

zpx 4

zpx 4

zpx 6

zpx 6

2

aby 4*

2

aby 7

abx 4*

abx 4*

abx 7

abx 7

abs 6

izx 6

izx 8

zp 3

zp 3

zp 5

zp 5

4

imm 2

2

imm 2

abs 4

abs 4

abs 6

abs 6

rel 2*

izy 5*

izy 8

zpx 4

zpx 4

zpx 6

zpx 6

2

aby 4*

2

aby 7

abx 4*

abx 4*

abx 7

abx 7

6

izx 6

izx 8

zp 3

zp 3

zp 5

zp 5

3

imm 2

2

imm 2

abs 3

abs 4

abs 6

abs 6

rel 2*

izy 5*

izy 8

zpx 4

zpx 4

zpx 6

zpx 6

2

aby 4*

2

aby 7

abx 4*

abx 4*

abx 7

abx 7

6

izx 6

izx 8

zp 3

zp 3

zp 5

zp 5

4

imm 2

2

imm 2

ind 5

abs 4

abs 6

abs 6

rel 2*

izy 5*

izy 8

zpx 4

zpx 4

zpx 6

zpx 6

2

aby 4*

2

aby 7

abx 4*

abx 4*

abx 7

abx 7

imm 2

izx 6

imm 2

izx 6

zp 3

zp 3

zp 3

zp 3

2

imm 2

2

imm 2

abs 4

abs 4

abs 4

abs 4

rel 2*

izy 6

izy 6

zpx 4

zpx 4

zpy 4

zpy 4

2

aby 5

2

aby 5

abx 5

abx 5

aby 5

aby 5

imm 2

izx 6

imm 2

izx 6

zp 3

zp 3

zp 3

zp 3

2

imm 2

2

imm 2

abs 4

abs 4

abs 4

abs 4

rel 2*

izy 5*

izy 5*

zpx 4

zpx 4

zpy 4

zpy 4

2

aby 4*

2

aby 4*

abx 4*

abx 4*

aby 4*

aby 4*

imm 2

izx 6

imm 2

izx 8

zp 3

zp 3

zp 5

zp 5

2

imm 2

2

imm 2

abs 4

abs 4

abs 6

abs 6

rel 2*

izy 5*

izy 8

zpx 4

zpx 4

zpx 6

zpx 6

2

aby 4*

2

aby 7

abx 4*

abx 4*

abx 7

abx 7

imm 2

izx 6

imm 2

izx 8

zp 3

zp 3

zp 5

zp 5

2

imm 2

2

imm 2

abs 4

abs 4

abs 6

abs 6

rel 2*

izy 5*

izy 8

zpx 4

zpx 4

zpx 6

zpx 6

2

aby 4*

2

aby 7

abx 4*

abx 4*

abx 7

abx 7

"*" : add 1 cycle if page boundary is crossed.

add 1 cycle on branches if taken.

Logical and arithmetic commands:

Opcode | imp | imm | zp | zpx | zpy | izx | izy | abs | abx | aby | ind | rel | Function | N | V | B | D | I | Z | C |

ORA | $09 | $05 | $15 | $01 | $11 | $0D | $1D | $19 | A:=A or {adr} | * | * | |||||||||

AND | $29 | $25 | $35 | $21 | $31 | $2D | $3D | $39 | A:=A&{adr} | * | * | |||||||||

EOR | $49 | $45 | $55 | $41 | $51 | $4D | $5D | $59 | A:=A exor {adr} | * | * | |||||||||

ADC | $69 | $65 | $75 | $61 | $71 | $6D | $7D | $79 | A:=A+{adr} | * | * | * | * | |||||||

SBC | $E9 | $E5 | $F5 | $E1 | $F1 | $ED | $FD | $F9 | A:=A-{adr} | * | * | * | * | |||||||

CMP | $C9 | $C5 | $D5 | $C1 | $D1 | $CD | $DD | $D9 | A-{adr} | * | * | * | ||||||||

CPX | $E0 | $E4 | $EC | X-{adr} | * | * | * | |||||||||||||

CPY | $C0 | $C4 | $CC | Y-{adr} | * | * | * | |||||||||||||

DEC | $C6 | $D6 | $CE | $DE | {adr}:={adr}-1 | * | * | |||||||||||||

DEX | $CA | X:=X-1 | * | * | ||||||||||||||||

DEY | $88 | Y:=Y-1 | * | * | ||||||||||||||||

INC | $E6 | $F6 | $EE | $FE | {adr}:={adr}+1 | * | * | |||||||||||||

INX | $E8 | X:=X+1 | * | * | ||||||||||||||||

INY | $C8 | Y:=Y+1 | * | * | ||||||||||||||||

ASL | $0A | $06 | $16 | $0E | $1E | {adr}:={adr}*2 | * | * | * | |||||||||||

ROL | $2A | $26 | $36 | $2E | $3E | {adr}:={adr}*2+C | * | * | * | |||||||||||

LSR | $4A | $46 | $56 | $4E | $5E | {adr}:={adr}/2 | * | * | * | |||||||||||

ROR | $6A | $66 | $76 | $6E | $7E | {adr}:={adr}/2+C*128 | * | * | * |

Move commands:

Opcode | imp | imm | zp | zpx | zpy | izx | izy | abs | abx | aby | ind | rel | Function | N | V | B | D | I | Z | C |

LDA | $A9 | $A5 | $B5 | $A1 | $B1 | $AD | $BD | $B9 | A:={adr} | * | * | |||||||||

STA | $85 | $95 | $81 | $91 | $8D | $9D | $99 | {adr}:=A | ||||||||||||

LDX | $A2 | $A6 | $B6 | $AE | $BE | X:={adr} | * | * | ||||||||||||

STX | $86 | $96 | $8E | {adr}:=X | ||||||||||||||||

LDY | $A0 | $A4 | $B4 | $AC | $BC | Y:={adr} | * | * | ||||||||||||

STY | $84 | $94 | $8C | {adr}:=Y | ||||||||||||||||

TAX | $AA | X:=A | * | * | ||||||||||||||||

TXA | $8A | A:=X | * | * | ||||||||||||||||

TAY | $A8 | Y:=A | * | * | ||||||||||||||||

TYA | $98 | A:=Y | * | * | ||||||||||||||||

TSX | $BA | X:=S | * | * | ||||||||||||||||

TXS | $9A | S:=X | ||||||||||||||||||

PLA | $68 | A:=+(S) | * | * | ||||||||||||||||

PHA | $48 | (S)-:=A | ||||||||||||||||||

PLP | $28 | P:=+(S) | * | * | * | * | * | * | ||||||||||||

PHP | $08 | (S)-:=P |

Jump/Flag commands:

Opcode | imp | imm | zp | zpx | zpy | izx | izy | abs | abx | aby | ind | rel | Function | N | V | B | D | I | Z | C |

BPL | $10 | branch on N=0 | ||||||||||||||||||

BMI | $30 | branch on N=1 | ||||||||||||||||||

BVC | $50 | branch on V=0 | ||||||||||||||||||

BVS | $70 | branch on V=1 | ||||||||||||||||||

BCC | $90 | branch on C=0 | ||||||||||||||||||

BCS | $B0 | branch on C=1 | ||||||||||||||||||

BNE | $D0 | branch on Z=0 | ||||||||||||||||||

BEQ | $F0 | branch on Z=1 | ||||||||||||||||||

BRK | $00 | (S)-=:PC,P PC:=($FFFE) | 1 | 1 | ||||||||||||||||

RTI | $40 | P,PC:=+(S) | * | * | * | * | * | * | ||||||||||||

JSR | $20 | (S)-:=PC PC:={adr} | ||||||||||||||||||

RTS | $60 | PC:=+(S) | ||||||||||||||||||

JMP | $4C | $6C | PC:={adr} | |||||||||||||||||

BIT | $24 | $34 | $2C | $3C | N:=b7 V:=b6 Z:=A&{adr} | * | * | * | ||||||||||||

CLC | $18 | C:=0 | 0 | |||||||||||||||||

SEC | $38 | C:=1 | 1 | |||||||||||||||||

CLD | $D8 | D:=0 | 0 | |||||||||||||||||

SED | $F8 | D:=1 | 1 | |||||||||||||||||

CLI | $58 | I:=0 | 0 | |||||||||||||||||

SEI | $78 | I:=1 | 1 | |||||||||||||||||

CLV | $B8 | V:=0 | 0 | |||||||||||||||||

NOP | $EA |

Flags of the status register:

The processor status register has 8 bits, where 7 are used as flags:

N = negative flag (1 when result is negative)

V = overflow flag (1 on signed overflow)

# = unused (always 1)

B = break flag (1 when interupt was caused by a BRK)

D = decimal flag (1 when CPU in BCD mode)

I = IRQ flag (when 1, no interupts will occur (exceptions are IRQs forced by BRK and NMIs))

Z = zero flag (1 when all bits of a result are 0)

C = carry flag (1 on unsigned overflow)

Hardware vectors:

$FFFA = NMI vector (NMI=not maskable interupts)

$FFFC = Reset vector

$FFFE = IRQ vector

Illegal opcodes:

Opcode | imp | imm | zp | zpx | zpy | izx | izy | abs | abx | aby | ind | rel | Function | N | V | B | D | I | Z | C |

SLO | $07 | $17 | $03 | $13 | $0F | $1F | $1B | {adr}:={adr}*2 A:=A or {adr} | * | * | * | |||||||||

RLA | $27 | $37 | $23 | $33 | $2F | $3F | $3B | {adr}:={adr}rol A:=A and {adr} | * | * | * | |||||||||

SRE | $47 | $57 | $43 | $53 | $4F | $5F | $5B | {adr}:={adr}/2 A:=A exor {adr} | * | * | * | |||||||||

RRA | $67 | $77 | $63 | $73 | $6F | $7F | $7B | {adr}:={adr}ror A:=A adc {adr} | * | * | * | * | ||||||||

SAX | $87 | $97 | $83 | $8F | {adr}:=A&X | |||||||||||||||

LAX | $A7 | $B7 | $A3 | $B3 | $AF | $BF | A,X:={adr} | * | * | |||||||||||

DCP | $C7 | $D7 | $C3 | $D3 | $CF | $DF | $DB | {adr}:={adr}-1 A-{adr} | * | * | * | |||||||||

ISC | $E7 | $F7 | $E3 | $F3 | $EF | $FF | $FB | {adr}:={adr}+1 A:=A-{adr} | * | * | * | * | ||||||||

ANC | $0B | A:=A&#{imm} | * | * | * | |||||||||||||||

ANC | $2B | A:=A&#{imm} | * | * | * | |||||||||||||||

ALR | $4B | A:=(A&#{imm})*2 | * | * | * | |||||||||||||||

ARR | $6B | A:=(A&#{imm})/2 | * | * | * | * | ||||||||||||||

XAA² | $8B | A:=X&#{imm} | * | * | ||||||||||||||||

LAX² | $AB | A,X:=#{imm} | * | * | ||||||||||||||||

AXS | $CB | X:=A&X-#{imm} | * | * | * | |||||||||||||||

SBC | $EB | A:=A-#{imm} | * | * | * | * | ||||||||||||||

AHX¹ | $93 | $9F | {adr}:=A&X&H | |||||||||||||||||

SHY¹ | $9C | {adr}:=Y&H | ||||||||||||||||||

SHX¹ | $9E | {adr}:=X&H | ||||||||||||||||||

TAS¹ | $9B | S:=A&X {adr}:=S&H | ||||||||||||||||||

LAS | $BB | A,X,S:={adr}&S | * | * |

² = highly unstable (results are not predictable on some machines)

A = Akkumulator

X = X-Register

Y = Y-Register

S = Stack-Pointer

P = Status-Register

+(S) = Stack-Pointer relative with pre-increment

(S)- = Stack-Pointer relative with post-decrement

Combinations of two operations with the same addressing mode:

RLA {adr} = ROL {adr} + AND {adr}

SRE {adr} = LSR {adr} + EOR {adr}

RRA {adr} = ROR {adr} + ADC {adr}

SAX {adr} = store A&X into {adr}

LAX {adr} = LDA {adr} + LDX {adr}

DCP {adr} = DEC {adr} + CMP {adr}

ISC {adr} = INC {adr} + SBC {adr}

note to SAX: the A&X operation is a result of A and X put onto the bus at the same time.

Combinations of an immediate and an implied command:

ANC #{imm} = AND #{imm} + (ROL)

ALR #{imm} = AND #{imm} + LSR

ARR #{imm} = AND #{imm} + ROR

XAA #{imm} = TXA + AND #{imm}

LAX #{imm} = LDA #{imm} + TAX

AXS #{imm} = A&X minus #{imm} into X

SBC #{imm} = SBC #{imm} + NOP

note to ANC: this command performs an AND operation only, but bit 7 is put into the carry, as if the ASL/ROL would have been executed.

note to ARR: part of this command are some ADC mechanisms. following effects appear after AND but before ROR: the V-Flag is set according to (A and #{imm})+#{imm}, bit 0 does NOT go into carry, but bit 7 is exchanged with the carry.

note to XAA: DO NOT USE!!! Highly unstable!!!

note to LAX: DO NOT USE!!! On my C128, this opcode is stable, but on my C64-II it loses bits so that the operation looks like this: ORA #? AND #{imm} TAX.

note to AXS: performs CMP and DEX at the same time, so that the MINUS sets the flag like CMP, not SBC.

Combinations of STA/STX/STY:

SHX {adr} = stores X&H into {adr}

SHY {adr} = stores Y&H into {adr}

note: sometimes the &H drops off. Also page boundary crossing will not work as expected (the bank where the value is stored may be equal to the value stored).

Combinations of STA/TXS and LDA/TSX:

LAS {adr} = stores {adr}&S into A, X and S

note to LAS: is called as "propably unreliable" in one source.

Bit configuration does not allow any operation on these ones:

NOP #{imm} = fetches #{imm} but has no effects

NOP {adr} = fetches {adr} but has no effects

KIL = halts the CPU. the data bus will be set to #$FF

Aliases used in other illegal opcode sources:

SLO = ASO

SRE = LSE

ISC = ISB

ALR = ASR

SHX = A11 (A11 was a result of only having tested this one on adress $1000)

SHY = A11

LAS = LAR

KIL = JAM, HLT

The 6502 bugs:

Zeropage index will not leave zeropage when page boundary is crossed:

LDA $FF,X

...will fetch from adress $0000 and not $0100 as indexed.

Indirect adressing modes are not able to fetch an adress which crosses the page boundary:

Four examples to illustrate this:

LDX #$00

LDA ($FF,X)

LDX #$FF

LDA ($00,X)

... will all fetch the low-byte from $00FF and the high-byte from $0000

... will fetch the low-byte from $12FF and the high-byte from $1200

The N, V and Z flags do not work correctly in BCD mode:

N will always carry bit 7.

V will always be ((U eor N) nand (U eor V)) (while U is bit 7 of operand 1, V is bit 7 of operand 2 and N is the N flag after the ADC is performed).

please note that SBC is truly ADC with an inverted operand!

Z will be 0 when the non-BCD operation WOULD have resulted in $00, no matter what value the result of the BCD operation is.

example to Z:

CLC

LDA #$80

ADC #$80

... results in A=$60, but the Z flag is 1.

BCD and non BCD values:

Since only nibble values from 0 to 9 are valid in BCD, it's interesting to see what happens when using A to F:

$00+$1F=$25 (can be claimed as being "ok" since 10+$0F=25)

$10+$1F=$35 ("ok")

$05+$1F=$2A (a non-BCD result, still somewhat "ok" since 5+10+$0F=20+$0A)

$0F+$0A=$1F ("ok", since $0F+$0A=$0F+10)

$0F+$0B=$10 (now, this is plain bullshit!)

Details about BRK, RTI, JSR and RTS:

It may sound confusing to a lot of people, but BRK, RTI and RTS are only virtually implied commands.

To explain this, let's have a look at JSR:

It fetches the two bytes following the opcode and stores them temporarely inside the CPU. Now it writes the two bytes of the PC onto the stack, and then loads the temporarely stored adress into the PC.

Exactly the same happens while RTS, with only one small difference:

It fetches the two bytes following the opcode and stores them temporarely inside the CPU. Now it FETCHES two bytes from the stack, stores them temporarely (which overwrites the previously fetched bytes) and then loads these into the PC.

Having a look at JSR and BRK, you get a clue about this behaviour:

$1003 ...

$1000 BRK --- will ALSO write $1002 onto the stack!!!

$1001 ...

... well, this could mean, that also BRK, RTI and RTS run with absolute adressing mode. Both, RTI and RTS, simply overwrite the fetched adresses, and BRK has a hardwired $FFFE/$FFFF as fetch adress.

How to prove the stated facts? Actually, this was no easy question until I got aware of the fact, that the C128's 2MHz-mode actually takes both, the VIC and the CPU bus cycles. This means, that the VIC will passively display what the CPU is doing, so I took a nested raster-IRQ and checked it out. I made the two bytes behind a RTS/BRK/RTI visible by incrementing them once per frame. This proves that the CPU fetches these bytes. (Actually only one of the two bytes is visible, as only every second cycle is a VIC cycle...)

Different versions of the 6502:

In the C64/C128 series of computers, slightly modified versions of the 6502 were used. The modifications did not affect the functional part of the processor itself. Only a so-called processor port was added. This port, in combination with an external PLA, was used to map ROM and I/O areas into the 64KB RAM of the C64. Also, some bits of the port were used for the legendary Datasette.

The port can be accessed through memory adresses $0000 and $0001, while $0001 is the port itself, and $0000 is the data direction register for it.

Explanation for the bits of $0001:

7 - unused (Flash 8: 0=8MHz/1=1MHz)

6 - unused (C128: ASCII/DIN sense/switch (1=ASCII/0=DIN))

5 - Cassette motor control (0 = motor on)

4 - Cassette switch sense (0 = PLAY pressed)

3 - Cassette write line

2 - CHAREN (0=Character ROM instead of I/O area)

1 - HIRAM ($E000-$FFFF)

0 - LORAM ($A000-$BFFF)

If HIRAM or LORAM is set, the I/O area is mapped to $D000-$DFFF.

$0000 should always be set to $2F (%00101111)

Note to bit 6: This bit is used to select either the ASCII or the DIN character ROM of a C128. When data direction is set to INPUT, the charset is selected externally with the ASCII/DIN key.

CPU versions:

6502: Used in the CBM floppies and some other 8 bit computers.

6510: Used in older C64 models.

8500: Did his job in C64-II models, basically the same processor, but different pin layout.

8502: This one was used in C128s.

All of these processors are the same concerning the software-side.

Some processors of the family which are not 100% compatible:

65C02: Extension of the 6502, used in the C16, C116 and the Plus/4 computers.

65SC02: Small version of the 65C02 which lost a few opcodes again.

65CE02: Extension of the 65C02, used in the C65.

65816: Extended 6502 with new opcodes and 16 bit operation modes.

Zeropage/Stack:

The first 256 bytes of adressable memory are called Zeropage. The 6502 processor family offers a wide selection of adressing modes to work with this part of the memory, which generally results in shorter and (even more important) faster code.

Following the Zeropage, the next 256 bytes (located at $0100-$01FF) are used as processor stack. The stack function of this part is defined as it is in most other CPU's: Writing to stack will automatically decrement the stack pointer, while reading from it will increment it.