Registers
{
A | Accumulator | Main working register
X/Y | Index registers | Generally used for array access
S | Stack pointer | 16-bit pointer. Gives the location of the first free byte on the stack. Decrements when pushed to, increments when pulled from
DP | Direct page | Points to the start of the direct page area of RAM (typically $0000), used by 8-bit address modes. Must be set by TCD instruction
DB | Data bank | The bank used for 16-bit address modes (must be set by PLB instruction)
PB/PC | Program bank/counter | The bank/address of the next instruction to execute
PSR | Processor status register | Contains some flags concerning the CPU state:
{
1 | C: Carry flag | See ADC/SBC/CMP/LSR/ASL/ROR/ROL
2 | Z: Zero flag | Set if the result of an operation is zero
4 | I: IRQ disable | If set, disables IRQ (but not NMI nor BRK)
8 | D: Decimal mode | If set, binary-coded decimal is used for ADC/SBC operations
10h | X: Indices mode | If set, index registers are 8-bit with upper bytes cleared
20h | M: Memory/accumulator mode | If set, memory accesses and accumulator are 8-bit, upper byte of A is preserved
40h | V: Overflow flag | See ADC/SBC/BIT
80h | N: Negative flag | Set if the result of an operation is negative (most significant bit is set)
}
}
LDA/LDX/LDY
{
Load to A, X or Y from memory
Sets PSR.Z and PSR.N according to the value loaded
A9xxxx | LDA #xxxx | A = xxxx, if PSR.M (8-bit accumulator) is set, then x is only one byte
A2xxxx | LDX #xxxx | X = xxxx, if PSR.X (8-bit indices) is set, then x is only one byte
A0xxxx | LDY #xxxx | Y = xxxx, if PSR.X (8-bit indices) is set, then x is only one byte
AFxxxxbb | LDA bbxxxx | A = [bb:xxxx]
ADxxxx | LDA xxxx | A = [DB:xxxx]
AExxxx | LDX xxxx | X = [DB:xxxx]
ACxxxx | LDY xxxx | Y = [DB:xxxx]
A5xx | LDA xx | A = [00:00xx + DP]
A6xx | LDX xx | X = [00:00xx + DP]
A4xx | LDY xx | Y = [00:00xx + DP]
BFxxxxbb | LDA bbxxxx,X | A = [bb:xxxx + X]
BDxxxx | LDA xxxx,X | A = [DB:xxxx + X]
BCxxxx | LDY xxxx,X | Y = [DB:xxxx + X]
B9xxxx | LDA xxxx,Y | A = [DB:xxxx + Y]
BExxxx | LDX xxxx,Y | X = [DB:xxxx + Y]
B5xx | LDA xx,X | A = [00:00xx + DP + X]
B4xx | LDY xx,X | Y = [00:00xx + DP + X]
B6xx | LDX xx,Y | X = [00:00xx + DP + Y]
A3xx | LDA xx,S | A = [00:00xx + S]
A7xx | LDA [xx] | A = [bb:yyyy] where bb:yyyy = [00:00xx + DP]
B2xx | LDA (xx) | A = [DB:yyyy] where yyyy = [00:00xx + DP]
B7xx | LDA [xx],Y | A = [bb:yyyy + Y] where bb:yyyy = [00:00xx + DP]
B1xx | LDA (xx),Y | A = [DB:yyyy + Y] where yyyy = [00:00xx + DP]
A1xx | LDA (xx,X) | A = [DB:yyyy] where yyyy = [00:00xx + DP + X]
B3xx | LDA (xx,S),Y | A = [DB:yyyy + Y] where yyyy = [00:00xx + S]
}
STA/STX/STY/STZ
{
Store A/X/Y/0 to memory
No PSR flags are set
STZ uses PSR.M to determine how many zero bytes to write
8Fxxxxbb | STA bbxxxx | bb:xxxx = A
8Dxxxx | STA xxxx | DB:xxxx = A
8Exxxx | STX xxxx | DB:xxxx = X
8Cxxxx | STY xxxx | DB:xxxx = Y
9Cxxxx | STZ xxxx | DB:xxxx = 0
85xx | STA xx | 00:00xx + DP = A
86xx | STX xx | 00:00xx + DP = X
84xx | STY xx | 00:00xx + DP = Y
64xx | STZ xx | 00:00xx + DP = 0
9Fxxxxbb | STA bbxxxx,X | bb:xxxx + X = A
9Dxxxx | STA xxxx,X | DB:xxxx + X = A
9Exxxx | STZ xxxx,X | DB:xxxx + X = 0
99xxxx | STA xxxx,Y | DB:xxxx + Y = A
95xx | STA xx,X | 00:00xx + DP + X = A
94xx | STY xx,X | 00:00xx + DP + X = Y
74xx | STZ xx,X | 00:00xx + DP + X = 0
96xx | STX xx,Y | 00:00xx + DP + Y = X
83xx | STA xx,S | 00:00xx + S = A
87xx | STA [xx] | bb:yyyy = A where bb:yyyy = [00:00xx + DP]
92xx | STA (xx) | DB:yyyy = A where yyyy = [00:00xx + DP]
97xx | STA [xx],Y | bb:yyyy = A where bb:yyyy = [00:00xx + DP]
91xx | STA (xx),Y | DB:yyyy = A where yyyy = [00:00xx + DP]
81xx | STA (xx,X) | DB:yyyy = A where yyyy = [00:00xx + DP + X]
93xx | STA (xx,S),Y | DB:yyyy + Y = A where yyyy = [00:00xx + S]
}
CMP/CPX/CPY
{
Compare A, X or Y with memory
Sets PSR.Z/PSR.N/PSR.C according to the result of subtracting the value from A/X/Y
Unlike SBC, this operation isn't affected by PSR.C and doesn't set PSR.V
Let lhs = A/X/Y and rhs = the compared value:
PSR.Z is set if lhs - rhs = 0
PSR.N is set if 8000h <= lhs - rhs <= FFFFh
PSR.C is set if subtraction doesn't exceed the range of unsigned numbers, i.e. unsigned(lhs) - unsigned(rhs) >= 0 (like ARM and unlike x86/z80)
Thus:
If lhs = rhs, then PSR.Z = 1
If unsigned(lhs) >= unsigned(rhs), then PSR.C = 1
If signed(lhs) >= signed(rhs), then PSR.N = 0 (assuming no overflow)
C9xxxx | CMP #xxxx | A - xxxx, if PSR.M (8-bit accumulator) is set, then x is only one byte
E0xxxx | CPX #xxxx | X - xxxx, if PSR.X (8-bit indices) is set, then x is only one byte
C0xxxx | CPY #xxxx | Y - xxxx, if PSR.X (8-bit indices) is set, then x is only one byte
CFxxxxxx | CMP bbxxxx | A - [bb:xxxx]
CDxxxx | CMP xxxx | A - [DB:xxxx]
ECxxxx | CPX xxxx | X - [DB:xxxx]
CCxxxx | CPY xxxx | Y - [DB:xxxx]
C5xx | CMP xx | A - [00:00xx + DP]
E4xx | CPX xx | X - [00:00xx + DP]
C4xx | CPY xx | Y - [00:00xx + DP]
DFxxxxxx | CMP bbxxxx,X | A - [bb:xxxx + X]
DDxxxx | CMP xxxx,X | A - [DB:xxxx + X]
D9xxxx | CMP xxxx,Y | A - [DB:xxxx + Y]
D5xx | CMP xx,X | A - [00:00xx + DP + X]
C3xx | CMP xx,S | A - [00:00xx + S]
C7xx | CMP [xx] | A - [bb:yyyy] where bb:yyyy = [00:00xx + DP]
D2xx | CMP (xx) | A - [DB:yyyy] where yyyy = [00:00xx + DP]
D7xx | CMP [xx],Y | A - [bb:yyyy + Y] where bb:yyyy = [00:00xx + DP]
D1xx | CMP (xx),Y | A - [DB:yyyy + Y] where yyyy = [00:00xx + DP]
C1xx | CMP (xx,X) | A - [DB:yyyy] where yyyy = [00:00xx + DP + X]
D3xx | CMP (xx,S),Y | A - [DB:yyyy + Y] where yyyy = [00:00xx + S]
}
ADC/SBC/AND/ORA/EOR
{
ADC: Add with carry
SBC: Subtract with carry
AND: Bitwise AND
ORA: Bitwise OR
EOR: Bitwise XOR
ADC/SBC notes
{
Let lhs = A and rhs = the memory operand:
SBC sets the carry/overflow flags the same way CMP does:
PSR.C is set if subtraction doesn't exceed the range of unsigned numbers, i.e. unsigned(lhs) - unsigned(rhs) + PSR.C - 1 >= 0 (like ARM and unlike x86/z80)
PSR.V is set if subtraction exceeds the range of signed numbers, i.e. not 8000h <= signed(lhs) - signed(rhs) + PSR.C - 1 <= 7FFFh
ADC sets the carry/overflow flags as:
PSR.C is set if addition exceeds the range of unsigned numbers, i.e. unsigned(lhs) + unsigned(rhs) + PSR.C > FFFFh
PSR.V is set if addition exceeds the range of signed numbers, i.e. not 8000h <= signed(lhs) + signed(rhs) + PSR.C <= 7FFFh
The reason ADC/SBC involves the carry is for multiprecision variables. E.g.:
Suppose:
$02 contains a 16-bit position
$00 contains a 16-bit subpixel position (where there are 10000h subpixels per pixel)
$12 contains a 16-bit speed
$10 contains a 16-bit subpixel speed
Then to add the speed to the position, one would do:
CLC ; Clear the carry so that the first addition is correct
LDA $00 : ADC $10 : STA $00 ; Add the subpixel values, the carry is propagated to the next ADC
LDA $02 : ADC $12 : STA $02 ; Add the pixel values along with the carry
To subtract the speed, one would do:
SEC
LDA $00 : SBC $10 : STA $00
LDA $02 : SBC $12 : STA $02
}
69xxxx | ADC #xxxx | A = A + xxxx + PSR.C, if PSR.M (8-bit accumulator) is set, then x is only one byte
E9xxxx | SBC #xxxx | A = A - xxxx + PSR.C - 1, if PSR.M (8-bit accumulator) is set, then x is only one byte
29xxxx | AND #xxxx | A = A AND xxxx, if PSR.M (8-bit accumulator) is set, then x is only one byte
09xxxx | ORA #xxxx | A = A OR xxxx, if PSR.M (8-bit accumulator) is set, then x is only one byte
49xxxx | EOR #xxxx | A = A XOR xxxx, if PSR.M (8-bit accumulator) is set, then x is only one byte
6Fxxxxbb | ADC bbxxxx | A = A + [bb:xxxx] + PSR.C
EFxxxxbb | SBC bbxxxx | A = A - [bb:xxxx] + PSR.C - 1
2Fxxxxbb | AND bbxxxx | A = A AND [bb:xxxx]
0Fxxxxbb | ORA bbxxxx | A = A OR [bb:xxxx]
4Fxxxxbb | EOR bbxxxx | A = A XOR [bb:xxxx]
6Dxxxx | ADC xxxx | A = A + [DB:xxxx] + PSR.C
EDxxxx | SBC xxxx | A = A - [DB:xxxx] + PSR.C - 1
2Dxxxx | AND xxxx | A = A AND [DB:xxxx]
0Dxxxx | ORA xxxx | A = A OR [DB:xxxx]
4Dxxxx | EOR xxxx | A = A XOR [DB:xxxx]
65xx | ADC xx | A = A + [00:00xx + DP] + PSR.C
E5xx | SBC xx | A = A - [00:00xx + DP] + PSR.C - 1
25xx | AND xx | A = A AND [00:00xx + DP]
05xx | ORA xx | A = A OR [00:00xx + DP]
45xx | EOR xx | A = A XOR [00:00xx + DP]
7Fxxxxbb | ADC bbxxxx,X | A = A + [bb:xxxx + X] + PSR.C
FFxxxxbb | SBC bbxxxx,X | A = A - [bb:xxxx + X] + PSR.C - 1
3Fxxxxbb | AND bbxxxx,X | A = A AND [bb:xxxx + X]
1Fxxxxbb | ORA bbxxxx,X | A = A OR [bb:xxxx + X]
5Fxxxxbb | EOR bbxxxx,X | A = A XOR [bb:xxxx + X]
7Dxxxx | ADC xxxx,X | A = A + [DB:xxxx + X] + PSR.C
FDxxxx | SBC xxxx,X | A = A - [DB:xxxx + X] + PSR.C - 1
3Dxxxx | AND xxxx,X | A = A AND [DB:xxxx + X]
1Dxxxx | ORA xxxx,X | A = A OR [DB:xxxx + X]
5Dxxxx | EOR xxxx,X | A = A XOR [DB:xxxx + X]
79xxxx | ADC xxxx,Y | A = A + [DB:xxxx + Y] + PSR.C
F9xxxx | SBC xxxx,Y | A = A - [DB:xxxx + Y] + PSR.C - 1
39xxxx | AND xxxx,Y | A = A AND [DB:xxxx + Y]
19xxxx | ORA xxxx,Y | A = A OR [DB:xxxx + Y]
59xxxx | EOR xxxx,Y | A = A XOR [DB:xxxx + Y]
75xx | ADC xx,X | A = A + [00:00xx + DP + X] + PSR.C
F5xx | SBC xx,X | A = A - [00:00xx + DP + X] + PSR.C - 1
35xx | AND xx,X | A = A AND [00:00xx + DP + X]
15xx | ORA xx,X | A = A OR [00:00xx + DP + X]
55xx | EOR xx,X | A = A XOR [00:00xx + DP + X]
63xx | ADC xx,S | A = A + [00:00xx + S] + PSR.C
E3xx | SBC xx,S | A = A - [00:00xx + S] + PSR.C - 1
23xx | AND xx,S | A = A AND [00:00xx + S]
03xx | ORA xx,S | A = A OR [00:00xx + S]
43xx | EOR xx,S | A = A XOR [00:00xx + S]
67xx | ADC [xx] | A = A + [bb:yyyy] + PSR.C where bb:yyyy = [00:00xx + DP]
E7xx | SBC [xx] | A = A - [bb:yyyy] + PSR.C - 1 where bb:yyyy = [00:00xx + DP]
27xx | AND [xx] | A = A AND [bb:yyyy] where bb:yyyy = [00:00xx + DP]
07xx | ORA [xx] | A = A OR [bb:yyyy] where bb:yyyy = [00:00xx + DP]
47xx | EOR [xx] | A = A XOR [bb:yyyy] where bb:yyyy = [00:00xx + DP]
72xx | ADC (xx) | A = A + [DB:yyyy] + PSR.C where yyyy = [00:00xx + DP]
F2xx | SBC (xx) | A = A - [DB:yyyy] + PSR.C - 1 where yyyy = [00:00xx + DP]
32xx | AND (xx) | A = A AND [DB:yyyy] where yyyy = [00:00xx + DP]
12xx | ORA (xx) | A = A OR [DB:yyyy] where yyyy = [00:00xx + DP]
52xx | EOR (xx) | A = A XOR [DB:yyyy] where yyyy = [00:00xx + DP]
77xx | ADC [xx],Y | A = A + [bb:yyyy + Y] + PSR.C where bb:yyyy = [00:00xx + DP]
F7xx | SBC [xx],Y | A = A - [bb:yyyy + Y] + PSR.C - 1 where bb:yyyy = [00:00xx + DP]
37xx | AND [xx],Y | A = A AND [bb:yyyy + Y] where bb:yyyy = [00:00xx + DP]
17xx | ORA [xx],Y | A = A OR [bb:yyyy + Y] where bb:yyyy = [00:00xx + DP]
57xx | EOR [xx],Y | A = A XOR [bb:yyyy + Y] where bb:yyyy = [00:00xx + DP]
71xx | ADC (xx),Y | A = A + [DB:yyyy + Y] + PSR.C where yyyy = [00:00xx + DP]
F1xx | SBC (xx),Y | A = A - [DB:yyyy + Y] + PSR.C - 1 where yyyy = [00:00xx + DP]
31xx | AND (xx),Y | A = A AND [DB:yyyy + Y] where yyyy = [00:00xx + DP]
11xx | ORA (xx),Y | A = A OR [DB:yyyy + Y] where yyyy = [00:00xx + DP]
51xx | EOR (xx),Y | A = A XOR [DB:yyyy + Y] where yyyy = [00:00xx + DP]
61xx | ADC (xx,X) | A = A + [DB:yyyy] + PSR.C where yyyy = [00:00xx + DP + X]
E1xx | SBC (xx,X) | A = A - [DB:yyyy] + PSR.C - 1 where yyyy = [00:00xx + DP + X]
21xx | AND (xx,X) | A = A AND [DB:yyyy] where yyyy = [00:00xx + DP + X]
01xx | ORA (xx,X) | A = A OR [DB:yyyy] where yyyy = [00:00xx + DP + X]
41xx | EOR (xx,X) | A = A XOR [DB:yyyy] where yyyy = [00:00xx + DP + X]
73xx | ADC (xx,S),Y | A = A + [DB:yyyy + Y] + PSR.C where yyyy = [00:00xx + S]
F3xx | SBC (xx,S),Y | A = A - [DB:yyyy + Y] + PSR.C - 1 where yyyy = [00:00xx + S]
33xx | AND (xx,S),Y | A = A AND [DB:yyyy + Y] where yyyy = [00:00xx + S]
13xx | ORA (xx,S),Y | A = A OR [DB:yyyy + Y] where yyyy = [00:00xx + S]
53xx | EOR (xx,S),Y | A = A XOR [DB:yyyy + Y] where yyyy = [00:00xx + S]
}
BIT
{
Bit test A with memory
Sets PSR flags according to the result of a bitwise AND with memory
Immediate BIT (BIT #xxxx) does not set the negative nor overflow flag (but still sets the zero flag)
Otherwise:
The negative flag is set to the most significant bit of the memory value
The overflow flag is set to the second-most significant bit of the memory value
89xxxx | BIT #xxxx | A AND #xxxx, if PSR.M (8-bit accumulator) is set, then x is only one byte
2Cxxxx | BIT xxxx | A AND [DB:xxxx]
24xx | BIT xx | A AND [00:00xx + DP]
3Cxxxx | BIT xxxx,X | A AND [DB:xxxx + X]
34xx | BIT xx,X | A AND [00:00xx+DP + X]
}
TSB/TRB
{
Test and set/reset bits of memory
Sets PSR.Z according to the value of the memory address *before* modification
PSR.N is *not* affected by these instructions
0Cxxxx | TSB xxxx | DB:xxxx = [DB:xxxx] OR A
1Cxxxx | TRB xxxx | DB:xxxx = [DB:xxxx] AND NOT A
04xx | TSB xx | 00:00xx + DP = [00:00xx + DP] OR A
14xx | TRB xx | 00:00xx + DP = [00:00xx + DP] AND NOT A
}
INC/INX/INY / DEC/DEX/DEY / ASL/LSR/ROL/ROR
{
INC/INX/INY: Increment A/memory / X / Y
DEC/DEX/DEY: Decrement A/memory / X / Y
ASL: (Arithmetic) shift left
LSR: Logical shift right
ROL: Rotate left with carry
ROR: Rotate right with carry
INC/INX/INY / DEC/DEX/DEY set PSR.Z and PSR.N according to the result, but don't modify PSR.C nor PSR.V
ASL/LSR set PSR.Z and PSR.N according to the result and sets PSR.C to the bit that was shifted out
ROL/ROR are like ASL/LSR instructions that operate on the 17-bit / 9-bit concatenation of A with PSR.C,
that is, the bit shifted out of A by ROL/ROR is shifted into PSR.C, the old PSR.C bit being shifted in to A
The reason ROL/ROR exists is for multiprecision variables. E.g.:
Suppose:
$02 contains a 16-bit speed
$00 contains a 16-bit subpixel speed (where there are 10000h subpixels per pixel)
Then to multiply the speed by 2, one would do ASL $00 : ROL $02
1A | INC A | A = A + 1
E8 | INX | X = X + 1
C8 | INY | Y = Y + 1
3A | DEC A | A = A - 1
CA | DEX | X = X - 1
88 | DEY | Y = Y - 1
0A | ASL A | A = A << 1
4A | LSR A | A = A >> 1
2A | ROL A | A = A << 1, A.LSb = PSR.C
6A | ROR A | A = A >> 1, A.MSb = PSR.C
EExxxx | INC xxxx | DB:xxxx = [DB:xxxx] + 1
CExxxx | DEC xxxx | DB:xxxx = [DB:xxxx] - 1
0Exxxx | ASL xxxx | DB:xxxx = [DB:xxxx] << 1
4Exxxx | LSR xxxx | DB:xxxx = [DB:xxxx] >> 1
2Exxxx | ROL xxxx | DB:xxxx = [DB:xxxx] << 1, (DB:xxxx).LSb = PSR.C
6Exxxx | ROR xxxx | DB:xxxx = [DB:xxxx] >> 1, (DB:xxxx).MSb = PSR.C
E6xx | INC xx | 00:00xx + DP = [00:00xx + DP] + 1
C6xx | DEC xx | 00:00xx + DP = [00:00xx + DP] - 1
06xx | ASL xx | 00:00xx + DP = [00:00xx + DP] << 1
46xx | LSR xx | 00:00xx + DP = [00:00xx + DP] >> 1
26xx | ROL xx | 00:00xx + DP = [00:00xx + DP] << 1, (00:00xx + DP).LSb = PSR.C
66xx | ROR xx | 00:00xx + DP = [00:00xx + DP] >> 1, (00:00xx + DP).MSb = PSR.C
FExxxx | INC xxxx,X | DB:xxxx + X = [DB:xxxx + X] + 1
DExxxx | DEC xxxx,X | DB:xxxx + X = [DB:xxxx + X] - 1
1Exxxx | ASL xxxx,X | DB:xxxx + X = [DB:xxxx + X] << 1
5Exxxx | LSR xxxx,X | DB:xxxx + X = [DB:xxxx + X] >> 1
3Exxxx | ROL xxxx,X | DB:xxxx + X = [DB:xxxx + X] << 1, (DB:xxxx + X).LSb = PSR.C
7Exxxx | ROR xxxx,X | DB:xxxx + X = [DB:xxxx + X] >> 1, (DB:xxxx + X).MSb = PSR.C
F6xx | INC xx,X | 00:00xx + DP + X = [00:00xx + DP + X] + 1
D6xx | DEC xx,X | 00:00xx + DP + X = [00:00xx + DP + X] - 1
16xx | ASL xx,X | 00:00xx + DP + X = [00:00xx + DP + X] << 1
56xx | LSR xx,X | 00:00xx + DP + X = [00:00xx + DP + X] >> 1
36xx | ROL xx,X | 00:00xx + DP + X = [00:00xx + DP + X] << 1, (00:00xx + DP + X).LSb = PSR.C
76xx | ROR xx,X | 00:00xx + DP + X = [00:00xx + DP + X] >> 1, (00:00xx + DP + X).MSb = PSR.C
}
BRL/BRA/BCC/BCS/BNE/BEQ/BPL/BMI/BVC/BVS
{
BRL: Branch long
BRA: Branch
BCC: Branch if carry clear
BCS: Branch if carry set
BNE: Branch if not equal
BEQ: Branch if equal
BPL: Branch if plus
BMI: Branch if minus
BVC: Branch if overflow clear
BVS: Branch if overflow set
No PSR flags are set
Note that BNE/BEQ can be misleading names, especially after AND/BIT. In practice, they're more like BZC/BZS
82xxxx | BRL xxxx | PC = PC + signed(xxxx)
80xx | BRA xx | PC = PC + signed(xx)
90xx | BCC xx | PC = PC + signed(xx) if PSR.C = 0
B0xx | BCS xx | PC = PC + signed(xx) if PSR.C = 1
D0xx | BNE xx | PC = PC + signed(xx) if PSR.Z = 0
F0xx | BEQ xx | PC = PC + signed(xx) if PSR.Z = 1
10xx | BPL xx | PC = PC + signed(xx) if PSR.N = 0
30xx | BMI xx | PC = PC + signed(xx) if PSR.N = 1
50xx | BVC xx | PC = PC + signed(xx) if PSR.V = 0
70xx | BVS xx | PC = PC + signed(xx) if PSR.V = 1
}
JMP/JML/JSR/JSL / COP/BRK / RTS/RTL/RTI
{
JMP: Jump
JML: Jump, long
JSR: Jump to subroutine
JSL: Jump to subroutine, long
COP: Coprocessor interrupt
BRK: Breakpoint interrupt
RTS: Return from subroutine
RTL: Return from subroutine, long
RTI: Return from interrupt
No PSR flags are set (except RTI/COP/BRK)
Reminder that PC refers to the next instruction to execute
Bug: JMP (xxFF) fetches the MSB from the same page, i.e. yyyy is fetched as [PB:xxFF] OR ([PB:xx00] << 8)
5Cxxxxbb | JML bbxxxx | PB:PC = bb:xxxx
4Cxxxx | JMP xxxx | PC = xxxx
DCxxxx | JML [xxxx] | PB:PC = bb:yyyy where bb:yyyy = [00:xxxx]
6Cxxxx | JMP (xxxx) | PC = yyyy where yyyy = [00:xxxx]
7Cxxxx | JMP (xxxx,X) | PC = yyyy where yyyy = [PB:xxxx + X]
22xxxxbb | JSL bbxxxx | Push PB:PC - 1, PB:PC = bb:xxxx
20xxxx | JSR xxxx | Push PC - 1, PC = xxxx
FCxxxx | JSR (xxxx,X) | Push PC - 1, PC = yyyy where yyyy = [PB:xxxx + X]
02xx | COP | Push PB:PC + 1, push PSR, PB:PC = 00:yyyy where yyyy = [00:FFF4], PSR.D = 0, PSR.I = 1
00xx | BRK | Push PB:PC + 1, push PSR, PB:PC = 00:yyyy where yyyy = [00:FFE6], PSR.D = 0, PSR.I = 1
60 | RTS | Pull to PC, PC = PC + 1
6B | RTL | Pull to PB:PC, PC = PC + 1
40 | RTI | Pull to PSR, pull to PB:PC
}
PEA/PEI/PER / PHA/PHX/PHY/PHP/PHD/PHB/PHK / PLA/PLX/PLY/PLP/PLD/PLB
{
PEA/PEI/PER: Push effective absolute/indirect/relative
PHA/PHX/PHY/PHP/PHD/PHB/PHK: Push A/X/Y/PSR/DP/DB/PB
PLA/PLX/PLY/PLP/PLD/PLB: Pull to A/X/Y/PSR/DP/DB
All pull instructions set the negative/zero flags (even PLB)
Pushing an n-byte register r onto the stack means:
S = S - n
[S + 1] = r
Conversely, pulling to an n-byte register from the stack means:
r = [S + 1]
S = S + n
F4xxxx | PEA xxxx | Push xxxx (does not depend on PSR.M)
D4xx | PEI (xx) | Push yyyy where yyyy = [00:00xx + DP]
62xxxx | PER xxxx | Push PC + signed(xxxx)
48 | PHA | Push A
DA | PHX | Push X
5A | PHY | Push Y
08 | PHP | Push PSR
0B | PHD | Push DP
8B | PHB | Push DB
4B | PHK | Push PB
68 | PLA | Pull to A
FA | PLX | Pull to X
7A | PLY | Pull to Y
28 | PLP | Pull to PSR
2B | PLD | Pull to DP
AB | PLB | Pull to DB
}
TAX/TAY/TCS/TCD/TXA/TXY/TXS/TYA/TYX/TSC/TSX/TDC / XBA
{
TAX/TAY/TCS/TCD: Transfer A to X/Y/S/DP
TXA/TXY: Transfer X to A/Y
TYA/TYX: Transfer Y to A/X
TSC/TSX: Transfer S to A/X
TDC: Transfer DP to A
XBA: Exchange bytes of A
TXS/TCS do *not* set any PSR flags
XBA sets the zero/negative flags according to the new LSB
AA | TAX | X = A
8A | TXA | A = X
A8 | TAY | Y = A
98 | TYA | A = Y
9B | TXY | Y = X
BB | TYX | X = Y
1B | TCS | S = A
3B | TSC | A = S
BA | TSX | X = S
9A | TXS | S = X
5B | TCD | DP = A
7B | TDC | A = DP
EB | XBA | A = (A AND FFh) << 8 OR (A >> 8)
}
SEP/REP / CLC/CLD/CLI/CLV / SEC/SED/SEI / XCE
{
SEP/REP: Set/reset PSR bits
CLC/CLD/CLI/CLV: Clear carry/decimal/IRQ/overflow flag
SEC/SED/SEI: Set carry/decimal/IRQ flag
XCE: Exchange carry and emulation flags
E2xx | SEP #xx | PSR = PSR OR xx
C2xx | REP #xx | PSR = PSR AND NOT xx
18 | CLC | Clear PSR.C (carry)
D8 | CLD | Clear PSR.D (decimal)
58 | CLI | Clear PSR.I (IRQ disable)
B8 | CLV | Clear PSR.V (overflow)
38 | SEC | Set PSR.C (carry)
F8 | SED | Set PSR.D (decimal)
78 | SEI | Set PSR.I (IRQ disable)
FB | XCE | Exchange carry and emulation flags (emulation flag not doc'd here)
}
MVN/MVP
{
Move memory in the negative/positive direction
Use MVN to move data backwards (to a lower address), use MVP to move data forwards (to a higher address)
After MVN/MVP, DB = yy, A = FFFFh
After MVN, X += A + 1, Y += A + 1
After MVP, X -= A + 1, Y -= A + 1
54yyxx | MVN xx yy | Copy A + 1 bytes from xx:X to yy:Y starting from the beginning
44yyxx | MVP xx yy | Copy A + 1 bytes from xx:X to yy:Y starting from the end
}
WAI/STP / NOP/WDM
{
WAI: Wait for interrupt
STP: Stop
NOP: No operation
WDM: William David Mensch Jr., 65816 designer
CB | WAI | Sleep until interrupt occurs (NMI or IRQ), will wake up due to IRQ even if IRQ is disabled via PSR.I = 1
DB | STP | Kill CPU
EA | NOP | Nothing
42xx | WDM | Reserved
}
Opcode table
{
For even n: 00 20 40 60 80 A0 C0 E0
n0 = ooo BRK RTI RTS
n0 = JSR xxxx JSR
n0 = BRA xx BRA
n0 = ooo #xxxx LDY CPY CPX
n1 = ooo (xx,X) ORA AND EOR ADC STA LDA CMP SBC
n2 = ooo COP WDM
n2 = JSR xxxxxx JSR
n2 = ooo xxxx PER BRL
n2 = LDX #xxxx LDX
n2 = oEP #xx REP SEP
n3 = ooo xx,S ORA AND EOR ADC STA LDA CMP SBC
n4 = ooo xx TSB BIT STZ STY LDY CPY CPX
n4 = MVP xx yy MVP
n5 = ooo xx ORA AND EOR ADC STA LDA CMP SBC
n6 = ooo xx ASL ROL LSR ROR STX LDX DEC INC
n7 = ooo [xx] ORA AND EOR ADC STA LDA CMP SBC
n8 = ooo PHP PLP PHA PLA DEY TAY INY INX
n9 = ooo #xxxx ORA AND EOR ADC BIT LDA CMP SBC
nA = ooo ASL ROL LSR ROR TXA TAX DEX NOP
nB = ooo PHD PLD PHK RTL PHB PLB WAI XBA
nC = ooo xxxx TSB BIT JMP STY LDY CPY CPX
nC = JMP (xxxx) JMP
nD = ooo xxxx ORA AND EOR ADC STA LDA CMP SBC
nE = ooo xxxx ASL ROL LSR ROR STX LDX DEC INC
nF = ooo xxxxxx ORA AND EOR ADC STA LDA CMP SBC
--------
For odd n: 10 30 50 70 90 B0 D0 F0
n0 = Boo xx BPL BMI BVC BVS BCC BCS BNE BEQ
n1 = ooo (xx),Y ORA AND EOR ADC STA LDA CMP SBC
n2 = ooo (xx) ORA AND EOR ADC STA LDA CMP SBC
n3 = ooo (xx,S),Y ORA AND EOR ADC STA LDA CMP SBC
n4 = ooo xx TRB
n4 = ooo xx,X BIT STZ STY LDY
n4 = MVN xx yy MVN
n4 = PEI (xx) PEI
n4 = PEA xxxx PEA
n5 = ooo xx,X ORA AND EOR ADC STA LDA CMP SBC
n6 = ooo xx,X ASL ROL LSR ROR STX LDX DEC INC
n7 = ooo [xx],Y ORA AND EOR ADC STA LDA CMP SBC
n8 = ooo CLC SEC CLI SEI TYA CLV CLD SED
n9 = ooo xxxx,Y ORA AND EOR ADC STA LDA CMP SBC
nA = ooo INC DEC PHY PLY TXS TSX PHX PLX
nB = ooo TCS TSC TCD TDC TXY TYX STP XCE
nC = ooo xxxx TRB STZ
nC = ooo xxxx,X BIT LDY
nC = JMP xxxxxx JMP
nC = Joo (xxxx,X) JMP JSR
nC = JMP [xxxx] JMP
nD = ooo xxxx,X ORA AND EOR ADC STA LDA CMP SBC
nE = ooo xxxx,X ASL ROL LSR ROR STZ DEC INC
nE = ooo xxxx,Y LDX
nF = ooo xxxxxx,X ORA AND EOR ADC STA LDA CMP SBC
}