Difference between revisions of "6502 opcodes for hackers"
(4 intermediate revisions by the same user not shown) | |||
Line 13: | Line 13: | ||
{| class="wikitable" style="text-align: center; font-family: monospace;" | {| class="wikitable" style="text-align: center; font-family: monospace;" | ||
|+ '''NMOS 6502 Opcode Matrix''' | |+ '''NMOS 6502 Opcode Matrix''' | ||
− | ! HI | + | ! HI\LO |
! -0 | ! -0 | ||
! -1 | ! -1 | ||
Line 323: | Line 323: | ||
This section covers the official, documented NMOS 6502 opcodes. | This section covers the official, documented NMOS 6502 opcodes. | ||
+ | <span id="ADC"></span> | ||
=== ADC (ADd with Carry) === | === ADC (ADd with Carry) === | ||
Adds memory and the Carry flag to the accumulator. To perform an 8-bit addition, a preceding `CLC` is required. The `V` flag detects signed overflow, and the `D` flag enables BCD arithmetic. | Adds memory and the Carry flag to the accumulator. To perform an 8-bit addition, a preceding `CLC` is required. The `V` flag detects signed overflow, and the `D` flag enables BCD arithmetic. | ||
Line 347: | Line 348: | ||
|} | |} | ||
+ | <span id="AND"></span> | ||
=== AND (bitwise AND with accumulator) === | === AND (bitwise AND with accumulator) === | ||
Performs a bitwise AND between the accumulator and a memory value. Primarily used to mask (clear) bits. | Performs a bitwise AND between the accumulator and a memory value. Primarily used to mask (clear) bits. | ||
Line 371: | Line 373: | ||
|} | |} | ||
+ | <span id="ASL"></span> | ||
=== ASL (Arithmetic Shift Left) === | === ASL (Arithmetic Shift Left) === | ||
Shifts an operand left by one bit. Bit 0 is cleared to 0, and the old bit 7 is shifted into the Carry flag. | Shifts an operand left by one bit. Bit 0 is cleared to 0, and the old bit 7 is shifted into the Carry flag. | ||
Line 389: | Line 392: | ||
|} | |} | ||
+ | <span id="BCC"></span><span id="BCS"></span><span id="BEQ"></span><span id="BNE"></span><span id="BMI"></span><span id="BPL"></span><span id="BVC"></span><span id="BVS"></span> | ||
=== Branch Instructions === | === Branch Instructions === | ||
Branch instructions alter program flow based on the state of a specific flag. All use relative addressing, taking a signed 8-bit offset. A branch costs 2 cycles if not taken, 3 if taken within the same page, and 4 if the destination crosses a page boundary. To create an unconditional branch (`BRA`), branch on a flag with a known state. The `V` flag is a good candidate, as it is unaffected by most operations: `CLV` followed by `BVC` will always branch. | Branch instructions alter program flow based on the state of a specific flag. All use relative addressing, taking a signed 8-bit offset. A branch costs 2 cycles if not taken, 3 if taken within the same page, and 4 if the destination crosses a page boundary. To create an unconditional branch (`BRA`), branch on a flag with a known state. The `V` flag is a good candidate, as it is unaffected by most operations: `CLV` followed by `BVC` will always branch. | ||
Line 413: | Line 417: | ||
|} | |} | ||
+ | <span id="BIT"></span> | ||
=== BIT (test BITS) === | === BIT (test BITS) === | ||
Performs a non-destructive `AND` between the accumulator and memory to set the Z flag, without altering any registers. It also copies bit 7 and bit 6 of the memory operand directly to the N and V flags, respectively. This makes it an essential tool for polling I/O registers. | Performs a non-destructive `AND` between the accumulator and memory to set the Z flag, without altering any registers. It also copies bit 7 and bit 6 of the memory operand directly to the N and V flags, respectively. This makes it an essential tool for polling I/O registers. | ||
Line 425: | Line 430: | ||
|} | |} | ||
+ | <span id="BRK"></span> | ||
=== BRK (BReaK) === | === BRK (BReaK) === | ||
Forces a software interrupt. It pushes PC+2 and the processor status (with the B flag set) to the stack, then jumps via the IRQ vector ($FFFE). An `RTI` returns to the address of `BRK + 2`, allowing it to replace a 2-byte instruction for debugging purposes. | Forces a software interrupt. It pushes PC+2 and the processor status (with the B flag set) to the stack, then jumps via the IRQ vector ($FFFE). An `RTI` returns to the address of `BRK + 2`, allowing it to replace a 2-byte instruction for debugging purposes. | ||
Line 441: | Line 447: | ||
* '''N flag''': Set to bit 7 of the subtraction result. | * '''N flag''': Set to bit 7 of the subtraction result. | ||
+ | <span id="CMP"></span> | ||
==== CMP (CoMPare accumulator) ==== | ==== CMP (CoMPare accumulator) ==== | ||
'''Affects Flags:''' N, Z, C | '''Affects Flags:''' N, Z, C | ||
Line 463: | Line 470: | ||
|} | |} | ||
+ | <span id="CPX"></span> | ||
==== CPX (ComPare X register) ==== | ==== CPX (ComPare X register) ==== | ||
'''Affects Flags:''' N, Z, C | '''Affects Flags:''' N, Z, C | ||
Line 475: | Line 483: | ||
|} | |} | ||
+ | <span id="CPY"></span> | ||
==== CPY (ComPare Y register) ==== | ==== CPY (ComPare Y register) ==== | ||
'''Affects Flags:''' N, Z, C | '''Affects Flags:''' N, Z, C | ||
Line 490: | Line 499: | ||
These modify a register or memory location by one. Register operations are 1-byte, 2-cycle instructions. Memory operations are read-modify-write and take longer. | These modify a register or memory location by one. Register operations are 1-byte, 2-cycle instructions. Memory operations are read-modify-write and take longer. | ||
+ | <span id="DEC"></span> | ||
==== DEC (DECrement memory) ==== | ==== DEC (DECrement memory) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 504: | Line 514: | ||
|} | |} | ||
+ | <span id="DEX"></span> | ||
==== DEX (DEcrement X register) ==== | ==== DEX (DEcrement X register) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 512: | Line 523: | ||
|} | |} | ||
+ | <span id="DEY"></span> | ||
==== DEY (DEcrement Y register) ==== | ==== DEY (DEcrement Y register) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 520: | Line 532: | ||
|} | |} | ||
+ | <span id="INC"></span> | ||
==== INC (INCrement memory) ==== | ==== INC (INCrement memory) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 534: | Line 547: | ||
|} | |} | ||
+ | <span id="INX"></span> | ||
==== INX (INcrement X register) ==== | ==== INX (INcrement X register) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 542: | Line 556: | ||
|} | |} | ||
+ | <span id="INY"></span> | ||
==== INY (INcrement Y register) ==== | ==== INY (INcrement Y register) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 550: | Line 565: | ||
|} | |} | ||
+ | <span id="EOR"></span> | ||
=== EOR (bitwise Exclusive OR) === | === EOR (bitwise Exclusive OR) === | ||
Performs a bitwise XOR between the accumulator and memory. Useful for flipping specific bits or for simple checksum calculations. | Performs a bitwise XOR between the accumulator and memory. Useful for flipping specific bits or for simple checksum calculations. | ||
Line 574: | Line 590: | ||
|} | |} | ||
+ | <span id="CLC"></span><span id="SEC"></span><span id="CLD"></span><span id="SED"></span><span id="CLI"></span><span id="SEI"></span><span id="CLV"></span> | ||
=== Flag Instructions === | === Flag Instructions === | ||
These single-byte, 2-cycle instructions directly manipulate the processor status flags. It is critical to use `CLD` on startup, as the power-on state of the D flag is undefined. | These single-byte, 2-cycle instructions directly manipulate the processor status flags. It is critical to use `CLD` on startup, as the power-on state of the D flag is undefined. | ||
Line 596: | Line 613: | ||
|} | |} | ||
+ | <span id="JMP"></span> | ||
=== JMP (JuMP) === | === JMP (JuMP) === | ||
Unconditionally transfers program control to a new location. The indirect mode has an infamous hardware bug: if the low byte of the indirect vector is $FF (e.g., `JMP ($30FF)`), the high byte of the jump address is fetched from `$3000`, not `$3100`. | Unconditionally transfers program control to a new location. The indirect mode has an infamous hardware bug: if the low byte of the indirect vector is $FF (e.g., `JMP ($30FF)`), the high byte of the jump address is fetched from `$3000`, not `$3100`. | ||
Line 608: | Line 626: | ||
|} | |} | ||
+ | <span id="JSR"></span> | ||
=== JSR (Jump to SubRoutine) === | === JSR (Jump to SubRoutine) === | ||
Pushes the return address (the address of the last byte of the `JSR` instruction) onto the stack and jumps to a new location. Paired with `RTS` for standard subroutine calls. | Pushes the return address (the address of the last byte of the `JSR` instruction) onto the stack and jumps to a new location. Paired with `RTS` for standard subroutine calls. | ||
Line 621: | Line 640: | ||
Load instructions copy a byte from memory into a register, setting the N and Z flags based on the value loaded. Note the addressing mode asymmetries: `LDX` has a `Zero Page,Y` mode while `LDY` has an `Absolute,X` mode, but not vice-versa. | Load instructions copy a byte from memory into a register, setting the N and Z flags based on the value loaded. Note the addressing mode asymmetries: `LDX` has a `Zero Page,Y` mode while `LDY` has an `Absolute,X` mode, but not vice-versa. | ||
+ | <span id="LDA"></span> | ||
==== LDA (LoaD Accumulator) ==== | ==== LDA (LoaD Accumulator) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 643: | Line 663: | ||
|} | |} | ||
+ | <span id="LDX"></span> | ||
==== LDX (LoaD X register) ==== | ==== LDX (LoaD X register) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 659: | Line 680: | ||
|} | |} | ||
+ | <span id="LDY"></span> | ||
==== LDY (LoaD Y register) ==== | ==== LDY (LoaD Y register) ==== | ||
'''Affects Flags:''' N, Z | '''Affects Flags:''' N, Z | ||
Line 675: | Line 697: | ||
|} | |} | ||
+ | <span id="LSR"></span> | ||
=== LSR (Logical Shift Right) === | === LSR (Logical Shift Right) === | ||
Shifts an operand right by one bit. Bit 7 is cleared to 0, and the old bit 0 is shifted into the Carry flag. | Shifts an operand right by one bit. Bit 7 is cleared to 0, and the old bit 0 is shifted into the Carry flag. | ||
Line 693: | Line 716: | ||
|} | |} | ||
+ | <span id="NOP"></span> | ||
=== NOP (No OPeration) === | === NOP (No OPeration) === | ||
The official NOP wastes 2 cycles and does nothing else. Many illegal opcodes also function as NOPs with different cycle and byte counts; some still perform memory reads, which can be useful for I/O timing or acknowledging interrupts without altering registers. | The official NOP wastes 2 cycles and does nothing else. Many illegal opcodes also function as NOPs with different cycle and byte counts; some still perform memory reads, which can be useful for I/O timing or acknowledging interrupts without altering registers. | ||
Line 703: | Line 727: | ||
|} | |} | ||
+ | <span id="ORA"></span> | ||
=== ORA (bitwise OR with Accumulator) === | === ORA (bitwise OR with Accumulator) === | ||
Performs a bitwise OR between the accumulator and a memory value. Primarily used to set bits. | Performs a bitwise OR between the accumulator and a memory value. Primarily used to set bits. | ||
Line 727: | Line 752: | ||
|} | |} | ||
+ | <span id="TAX"></span><span id="TAY"></span><span id="TXA"></span><span id="TYA"></span><span id="TSX"></span><span id="TXS"></span> | ||
=== Register Instructions === | === Register Instructions === | ||
These 1-byte, 2-cycle instructions transfer data between registers. `TXS` is unique in that it does not affect any flags. | These 1-byte, 2-cycle instructions transfer data between registers. `TXS` is unique in that it does not affect any flags. | ||
Line 747: | Line 773: | ||
|} | |} | ||
+ | <span id="ROL"></span> | ||
=== ROL (ROtate Left) === | === ROL (ROtate Left) === | ||
Rotates an operand left by one bit. The old bit 7 is moved to the Carry flag, and the old Carry flag is moved into bit 0. | Rotates an operand left by one bit. The old bit 7 is moved to the Carry flag, and the old Carry flag is moved into bit 0. | ||
Line 765: | Line 792: | ||
|} | |} | ||
+ | <span id="ROR"></span> | ||
=== ROR (ROtate Right) === | === ROR (ROtate Right) === | ||
Rotates an operand right by one bit. The old bit 0 is moved to the Carry flag, and the old Carry flag is moved into bit 7. | Rotates an operand right by one bit. The old bit 0 is moved to the Carry flag, and the old Carry flag is moved into bit 7. | ||
Line 783: | Line 811: | ||
|} | |} | ||
+ | <span id="RTI"></span> | ||
=== RTI (ReTurn from Interrupt) === | === RTI (ReTurn from Interrupt) === | ||
Restores the processor state after an interrupt by pulling the processor status register and program counter from the stack. | Restores the processor state after an interrupt by pulling the processor status register and program counter from the stack. | ||
Line 793: | Line 822: | ||
|} | |} | ||
+ | <span id="RTS"></span> | ||
=== RTS (ReTurn from Subroutine) === | === RTS (ReTurn from Subroutine) === | ||
Returns from a subroutine by pulling the program counter from the stack and incrementing it. Can be used to implement powerful jump tables by pushing crafted return addresses onto the stack. | Returns from a subroutine by pulling the program counter from the stack and incrementing it. Can be used to implement powerful jump tables by pushing crafted return addresses onto the stack. | ||
Line 803: | Line 833: | ||
|} | |} | ||
+ | <span id="SBC"></span> | ||
=== SBC (SuBtract with Carry) === | === SBC (SuBtract with Carry) === | ||
Subtracts memory and an inverted Carry flag (borrow) from the accumulator. To perform an 8-bit subtraction, a preceding `SEC` (to indicate "no borrow") is required. The `D` flag enables BCD arithmetic. | Subtracts memory and an inverted Carry flag (borrow) from the accumulator. To perform an 8-bit subtraction, a preceding `SEC` (to indicate "no borrow") is required. The `D` flag enables BCD arithmetic. | ||
Line 827: | Line 858: | ||
|} | |} | ||
+ | <span id="PHA"></span><span id="PLA"></span><span id="PHP"></span><span id="PLP"></span> | ||
=== Stack Instructions === | === Stack Instructions === | ||
These instructions interact with the hardware stack located at page one ($0100-$01FF). The 6502 stack pointer grows downwards. | These instructions interact with the hardware stack located at page one ($0100-$01FF). The 6502 stack pointer grows downwards. | ||
Line 846: | Line 878: | ||
Store instructions copy the content of a register to memory. They do not affect any flags. Note the addressing mode asymmetries, particularly the absence of accumulator-relative addressing and the presence of `STX zp,Y` and `STY zp,X`. | Store instructions copy the content of a register to memory. They do not affect any flags. Note the addressing mode asymmetries, particularly the absence of accumulator-relative addressing and the presence of `STX zp,Y` and `STY zp,X`. | ||
+ | <span id="STA"></span> | ||
==== STA (STore Accumulator) ==== | ==== STA (STore Accumulator) ==== | ||
'''Affects Flags:''' none | '''Affects Flags:''' none | ||
Line 866: | Line 899: | ||
|} | |} | ||
+ | <span id="STX"></span> | ||
==== STX (STore X register) ==== | ==== STX (STore X register) ==== | ||
'''Affects Flags:''' none | '''Affects Flags:''' none | ||
Line 878: | Line 912: | ||
|} | |} | ||
+ | <span id="STY"></span> | ||
==== STY (STore Y register) ==== | ==== STY (STore Y register) ==== | ||
'''Affects Flags:''' none | '''Affects Flags:''' none | ||
Line 893: | Line 928: | ||
This section details non-standard opcodes that are generally stable on NMOS 6502 and its direct variants. They often combine two standard operations into one, saving bytes and/or cycles. "Semi-stable" opcodes are marked and should be used with an understanding of their specific quirks, which are detailed in their descriptions. | This section details non-standard opcodes that are generally stable on NMOS 6502 and its direct variants. They often combine two standard operations into one, saving bytes and/or cycles. "Semi-stable" opcodes are marked and should be used with an understanding of their specific quirks, which are detailed in their descriptions. | ||
+ | <span id="SLO"></span> | ||
=== SLO (ASO) === | === SLO (ASO) === | ||
'''Function:''' ` {addr} = {addr} * 2`, then `A = A OR {addr}` (ASL followed by ORA) | '''Function:''' ` {addr} = {addr} * 2`, then `A = A OR {addr}` (ASL followed by ORA) | ||
Line 924: | Line 960: | ||
</pre> | </pre> | ||
+ | <span id="RLA"></span> | ||
=== RLA === | === RLA === | ||
'''Function:''' `{addr} = ROL {addr}`, then `A = A AND {addr}` (ROL followed by AND) | '''Function:''' `{addr} = ROL {addr}`, then `A = A AND {addr}` (ROL followed by AND) | ||
Line 959: | Line 996: | ||
</pre> | </pre> | ||
+ | <span id="SRE"></span> | ||
=== SRE (LSE) === | === SRE (LSE) === | ||
'''Function:''' `{addr} = LSR {addr}`, then `A = A EOR {addr}` (LSR followed by EOR) | '''Function:''' `{addr} = LSR {addr}`, then `A = A EOR {addr}` (LSR followed by EOR) | ||
Line 982: | Line 1,020: | ||
|} | |} | ||
+ | <span id="RRA"></span> | ||
=== RRA === | === RRA === | ||
'''Function:''' `{addr} = ROR {addr}`, then `A = A ADC {addr}` (ROR followed by ADC) | '''Function:''' `{addr} = ROR {addr}`, then `A = A ADC {addr}` (ROR followed by ADC) | ||
Line 1,005: | Line 1,044: | ||
|} | |} | ||
+ | <span id="SAX"></span> | ||
=== SAX (AXS) === | === SAX (AXS) === | ||
'''Function:''' `{addr} = A & X` | '''Function:''' `{addr} = A & X` | ||
Line 1,022: | Line 1,062: | ||
|} | |} | ||
+ | <span id="LAX"></span> | ||
=== LAX === | === LAX === | ||
'''Function:''' `A, X = {addr}` | '''Function:''' `A, X = {addr}` | ||
Line 1,043: | Line 1,084: | ||
|} | |} | ||
+ | <span id="DCP"></span> | ||
=== DCP (DCM) === | === DCP (DCM) === | ||
'''Function:''' `{addr} = {addr} - 1`, then `A CMP {addr}` (DEC followed by CMP) | '''Function:''' `{addr} = {addr} - 1`, then `A CMP {addr}` (DEC followed by CMP) | ||
Line 1,065: | Line 1,107: | ||
| Indirect,Y || DCP ($44),Y || $D3 || 2 || 8 | | Indirect,Y || DCP ($44),Y || $D3 || 2 || 8 | ||
|} | |} | ||
+ | '''Example:''' A loop condition that uses a decrementing memory location compared against another. | ||
+ | <pre> | ||
+ | ; Instead of DEC counter/ LDA counter / CMP value / BNE | ||
+ | LDA x1 | ||
+ | DCP x2 ;decrements x2 and compares x2 to A | ||
+ | BNE loopstart | ||
+ | </pre> | ||
− | + | <span id="ISC"></span> | |
=== ISC (ISB, INS) === | === ISC (ISB, INS) === | ||
'''Function:''' `{addr} = {addr} + 1`, then `A = A SBC {addr}` (INC followed by SBC) | '''Function:''' `{addr} = {addr} + 1`, then `A = A SBC {addr}` (INC followed by SBC) | ||
Line 1,099: | Line 1,148: | ||
</pre> | </pre> | ||
+ | <span id="ANC"></span> | ||
=== ANC === | === ANC === | ||
'''Function:''' `A = A & #{imm}`, then `C = N` | '''Function:''' `A = A & #{imm}`, then `C = N` | ||
Line 1,118: | Line 1,168: | ||
</pre> | </pre> | ||
+ | <span id="ALR"></span> | ||
=== ALR (ASR) === | === ALR (ASR) === | ||
'''Function:''' `A = (A & #{imm}) / 2` (AND immediate followed by LSR A) | '''Function:''' `A = (A & #{imm}) / 2` (AND immediate followed by LSR A) | ||
Line 1,129: | Line 1,180: | ||
|} | |} | ||
+ | <span id="ARR"></span> | ||
=== ARR === | === ARR === | ||
'''Function:''' `A = (A & #{imm})`, then a complex `ROR`-like operation. | '''Function:''' `A = (A & #{imm})`, then a complex `ROR`-like operation. | ||
Line 1,140: | Line 1,192: | ||
|} | |} | ||
+ | <span id="SBX"></span> | ||
=== SBX (AXS) === | === SBX (AXS) === | ||
'''Function:''' `X = (A & X) - #{imm}` | '''Function:''' `X = (A & X) - #{imm}` | ||
Line 1,168: | Line 1,221: | ||
# '''Hardware Dependency:''' The `AND` operation part of the instruction may fail if the CPU's RDY line is pulled low during a specific cycle (e.g., by sprite DMA). This makes the instruction behave like a standard store (`STA`/`STX`/`STY`). To mitigate this, one can use a target address where the high byte is `$FE`, so the `AND` with `(HighByte+1)` becomes an `AND` with `$FF`, which has no effect. | # '''Hardware Dependency:''' The `AND` operation part of the instruction may fail if the CPU's RDY line is pulled low during a specific cycle (e.g., by sprite DMA). This makes the instruction behave like a standard store (`STA`/`STX`/`STY`). To mitigate this, one can use a target address where the high byte is `$FE`, so the `AND` with `(HighByte+1)` becomes an `AND` with `$FF`, which has no effect. | ||
+ | <span id="SHA"></span> | ||
=== SHA (AXA, AHX) === | === SHA (AXA, AHX) === | ||
<span style="background-color:#FFFFC0;">(Semi-Stable)</span> | <span style="background-color:#FFFFC0;">(Semi-Stable)</span> | ||
Line 1,182: | Line 1,236: | ||
|} | |} | ||
+ | <span id="SHX"></span> | ||
=== SHX (SXA) === | === SHX (SXA) === | ||
<span style="background-color:#FFFFC0;">(Semi-Stable)</span> | <span style="background-color:#FFFFC0;">(Semi-Stable)</span> | ||
Line 1,194: | Line 1,249: | ||
|} | |} | ||
+ | <span id="SHY"></span> | ||
=== SHY (SYA) === | === SHY (SYA) === | ||
<span style="background-color:#FFFFC0;">(Semi-Stable)</span> | <span style="background-color:#FFFFC0;">(Semi-Stable)</span> | ||
Line 1,206: | Line 1,262: | ||
|} | |} | ||
+ | <span id="LAS"></span> | ||
=== LAS === | === LAS === | ||
<span style="background-color:#FFFFC0;">(Semi-Stable)</span> | <span style="background-color:#FFFFC0;">(Semi-Stable)</span> | ||
Line 1,218: | Line 1,275: | ||
|} | |} | ||
+ | <span id="TAS"></span> | ||
=== TAS (SHS) === | === TAS (SHS) === | ||
<span style="background-color:#FFFFC0;">(Semi-Stable)</span> | <span style="background-color:#FFFFC0;">(Semi-Stable)</span> |
Latest revision as of 21:23, 29 June 2025
6502 Opcodes for Hackers is a reference guide for experienced programmers. It focuses on the practical application of both standard and stable non-standard NMOS 6502 opcodes, including common tricks and non-obvious behaviors. It intentionally omits highly unstable opcodes to provide a focused, practical resource.
Contents
- 1 Opcode Matrix
- 2 Standard Opcodes
- 2.1 ADC (ADd with Carry)
- 2.2 AND (bitwise AND with accumulator)
- 2.3 ASL (Arithmetic Shift Left)
- 2.4 Branch Instructions
- 2.5 BIT (test BITS)
- 2.6 BRK (BReaK)
- 2.7 Compare Instructions
- 2.8 Decrement & Increment Instructions
- 2.9 EOR (bitwise Exclusive OR)
- 2.10 Flag Instructions
- 2.11 JMP (JuMP)
- 2.12 JSR (Jump to SubRoutine)
- 2.13 Load Instructions
- 2.14 LSR (Logical Shift Right)
- 2.15 NOP (No OPeration)
- 2.16 ORA (bitwise OR with Accumulator)
- 2.17 Register Instructions
- 2.18 ROL (ROtate Left)
- 2.19 ROR (ROtate Right)
- 2.20 RTI (ReTurn from Interrupt)
- 2.21 RTS (ReTurn from Subroutine)
- 2.22 SBC (SuBtract with Carry)
- 2.23 Stack Instructions
- 2.24 Store Instructions
- 3 Stable & Semi-Stable Illegal Opcodes
- 4 Attribution
Opcode Matrix
This table provides a complete overview of the NMOS 6502 opcode map. Each mnemonic links to its detailed description below.
Legend:
- Blue: Standard Opcodes
- Green: Stable Illegal Opcodes
- Yellow: Semi-Stable Illegal Opcodes (use with caution)
- Red: Unstable/Unreliable Opcodes (details not included below)
Standard Opcodes
This section covers the official, documented NMOS 6502 opcodes.
ADC (ADd with Carry)
Adds memory and the Carry flag to the accumulator. To perform an 8-bit addition, a preceding `CLC` is required. The `V` flag detects signed overflow, and the `D` flag enables BCD arithmetic. Affects Flags: N, V, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | ADC #$44 | $69 | 2 | 2 |
Zero Page | ADC $44 | $65 | 2 | 3 |
Zero Page,X | ADC $44,X | $75 | 2 | 4 |
Absolute | ADC $4400 | $6D | 3 | 4 |
Absolute,X | ADC $4400,X | $7D | 3 | 4+ |
Absolute,Y | ADC $4400,Y | $79 | 3 | 4+ |
Indirect,X | ADC ($44,X) | $61 | 2 | 6 |
Indirect,Y | ADC ($44),Y | $71 | 2 | 5+ |
AND (bitwise AND with accumulator)
Performs a bitwise AND between the accumulator and a memory value. Primarily used to mask (clear) bits. Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | AND #$44 | $29 | 2 | 2 |
Zero Page | AND $44 | $25 | 2 | 3 |
Zero Page,X | AND $44,X | $35 | 2 | 4 |
Absolute | AND $4400 | $2D | 3 | 4 |
Absolute,X | AND $4400,X | $3D | 3 | 4+ |
Absolute,Y | AND $4400,Y | $39 | 3 | 4+ |
Indirect,X | AND ($44,X) | $21 | 2 | 6 |
Indirect,Y | AND ($44),Y | $31 | 2 | 5+ |
ASL (Arithmetic Shift Left)
Shifts an operand left by one bit. Bit 0 is cleared to 0, and the old bit 7 is shifted into the Carry flag. Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Accumulator | ASL A | $0A | 1 | 2 |
Zero Page | ASL $44 | $06 | 2 | 5 |
Zero Page,X | ASL $44,X | $16 | 2 | 6 |
Absolute | ASL $4400 | $0E | 3 | 6 |
Absolute,X | ASL $4400,X | $1E | 3 | 7 |
Branch Instructions
Branch instructions alter program flow based on the state of a specific flag. All use relative addressing, taking a signed 8-bit offset. A branch costs 2 cycles if not taken, 3 if taken within the same page, and 4 if the destination crosses a page boundary. To create an unconditional branch (`BRA`), branch on a flag with a known state. The `V` flag is a good candidate, as it is unaffected by most operations: `CLV` followed by `BVC` will always branch. Affects Flags: none
Mnemonic | Name | HEX |
---|---|---|
BCC | Branch on Carry Clear (C=0) | $90 |
BCS | Branch on Carry Set (C=1) | $B0 |
BEQ | Branch on EQual (Z=1) | $F0 |
BNE | Branch on Not Equal (Z=0) | $D0 |
BMI | Branch on MInus (N=1) | $30 |
BPL | Branch on PLus (N=0) | $10 |
BVC | Branch on oVerflow Clear (V=0) | $50 |
BVS | Branch on oVerflow Set (V=1) | $70 |
BIT (test BITS)
Performs a non-destructive `AND` between the accumulator and memory to set the Z flag, without altering any registers. It also copies bit 7 and bit 6 of the memory operand directly to the N and V flags, respectively. This makes it an essential tool for polling I/O registers. Affects Flags: N, V, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | BIT $44 | $24 | 2 | 3 |
Absolute | BIT $4400 | $2C | 3 | 4 |
BRK (BReaK)
Forces a software interrupt. It pushes PC+2 and the processor status (with the B flag set) to the stack, then jumps via the IRQ vector ($FFFE). An `RTI` returns to the address of `BRK + 2`, allowing it to replace a 2-byte instruction for debugging purposes. Affects Flags: B
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | BRK | $00 | 1 | 7 |
Compare Instructions
These instructions perform a non-destructive subtraction (`Register - Memory`) to set the N, Z, and C flags. They are fundamental for conditional logic and comparisons.
- C flag: Set if `Register >= Memory` (no borrow).
- Z flag: Set if `Register == Memory`.
- N flag: Set to bit 7 of the subtraction result.
CMP (CoMPare accumulator)
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | CMP #$44 | $C9 | 2 | 2 |
Zero Page | CMP $44 | $C5 | 2 | 3 |
Zero Page,X | CMP $44,X | $D5 | 2 | 4 |
Absolute | CMP $4400 | $CD | 3 | 4 |
Absolute,X | CMP $4400,X | $DD | 3 | 4+ |
Absolute,Y | CMP $4400,Y | $D9 | 3 | 4+ |
Indirect,X | CMP ($44,X) | $C1 | 2 | 6 |
Indirect,Y | CMP ($44),Y | $D1 | 2 | 5+ |
CPX (ComPare X register)
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | CPX #$44 | $E0 | 2 | 2 |
Zero Page | CPX $44 | $E4 | 2 | 3 |
Absolute | CPX $4400 | $EC | 3 | 4 |
CPY (ComPare Y register)
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | CPY #$44 | $C0 | 2 | 2 |
Zero Page | CPY $44 | $C4 | 2 | 3 |
Absolute | CPY $4400 | $CC | 3 | 4 |
Decrement & Increment Instructions
These modify a register or memory location by one. Register operations are 1-byte, 2-cycle instructions. Memory operations are read-modify-write and take longer.
DEC (DECrement memory)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | DEC $44 | $C6 | 2 | 5 |
Zero Page,X | DEC $44,X | $D6 | 2 | 6 |
Absolute | DEC $4400 | $CE | 3 | 6 |
Absolute,X | DEC $4400,X | $DE | 3 | 7 |
DEX (DEcrement X register)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | DEX | $CA | 1 | 2 |
DEY (DEcrement Y register)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | DEY | $88 | 1 | 2 |
INC (INCrement memory)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | INC $44 | $E6 | 2 | 5 |
Zero Page,X | INC $44,X | $F6 | 2 | 6 |
Absolute | INC $4400 | $EE | 3 | 6 |
Absolute,X | INC $4400,X | $FE | 3 | 7 |
INX (INcrement X register)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | INX | $E8 | 1 | 2 |
INY (INcrement Y register)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | INY | $C8 | 1 | 2 |
EOR (bitwise Exclusive OR)
Performs a bitwise XOR between the accumulator and memory. Useful for flipping specific bits or for simple checksum calculations. Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | EOR #$44 | $49 | 2 | 2 |
Zero Page | EOR $44 | $45 | 2 | 3 |
Zero Page,X | EOR $44,X | $55 | 2 | 4 |
Absolute | EOR $4400 | $4D | 3 | 4 |
Absolute,X | EOR $4400,X | $5D | 3 | 4+ |
Absolute,Y | EOR $4400,Y | $59 | 3 | 4+ |
Indirect,X | EOR ($44,X) | $41 | 2 | 6 |
Indirect,Y | EOR ($44),Y | $51 | 2 | 5+ |
Flag Instructions
These single-byte, 2-cycle instructions directly manipulate the processor status flags. It is critical to use `CLD` on startup, as the power-on state of the D flag is undefined. Affects Flags: as noted
Mnemonic | Function | HEX |
---|---|---|
CLC | C = 0 | $18 |
SEC | C = 1 | $38 |
CLD | D = 0 | $D8 |
SED | D = 1 | $F8 |
CLI | I = 0 (Enable IRQs) | $58 |
SEI | I = 1 (Disable IRQs) | $78 |
CLV | V = 0 | $B8 |
JMP (JuMP)
Unconditionally transfers program control to a new location. The indirect mode has an infamous hardware bug: if the low byte of the indirect vector is $FF (e.g., `JMP ($30FF)`), the high byte of the jump address is fetched from `$3000`, not `$3100`. Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute | JMP $5597 | $4C | 3 | 3 |
Indirect | JMP ($5597) | $6C | 3 | 5 |
JSR (Jump to SubRoutine)
Pushes the return address (the address of the last byte of the `JSR` instruction) onto the stack and jumps to a new location. Paired with `RTS` for standard subroutine calls. Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute | JSR $5597 | $20 | 3 | 6 |
Load Instructions
Load instructions copy a byte from memory into a register, setting the N and Z flags based on the value loaded. Note the addressing mode asymmetries: `LDX` has a `Zero Page,Y` mode while `LDY` has an `Absolute,X` mode, but not vice-versa.
LDA (LoaD Accumulator)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | LDA #$44 | $A9 | 2 | 2 |
Zero Page | LDA $44 | $A5 | 2 | 3 |
Zero Page,X | LDA $44,X | $B5 | 2 | 4 |
Absolute | LDA $4400 | $AD | 3 | 4 |
Absolute,X | LDA $4400,X | $BD | 3 | 4+ |
Absolute,Y | LDA $4400,Y | $B9 | 3 | 4+ |
Indirect,X | LDA ($44,X) | $A1 | 2 | 6 |
Indirect,Y | LDA ($44),Y | $B1 | 2 | 5+ |
LDX (LoaD X register)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | LDX #$44 | $A2 | 2 | 2 |
Zero Page | LDX $44 | $A6 | 2 | 3 |
Zero Page,Y | LDX $44,Y | $B6 | 2 | 4 |
Absolute | LDX $4400 | $AE | 3 | 4 |
Absolute,Y | LDX $4400,Y | $BE | 3 | 4+ |
LDY (LoaD Y register)
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | LDY #$44 | $A0 | 2 | 2 |
Zero Page | LDY $44 | $A4 | 2 | 3 |
Zero Page,X | LDY $44,X | $B4 | 2 | 4 |
Absolute | LDY $4400 | $AC | 3 | 4 |
Absolute,X | LDY $4400,X | $BC | 3 | 4+ |
LSR (Logical Shift Right)
Shifts an operand right by one bit. Bit 7 is cleared to 0, and the old bit 0 is shifted into the Carry flag. Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Accumulator | LSR A | $4A | 1 | 2 |
Zero Page | LSR $44 | $46 | 2 | 5 |
Zero Page,X | LSR $44,X | $56 | 2 | 6 |
Absolute | LSR $4400 | $4E | 3 | 6 |
Absolute,X | LSR $4400,X | $5E | 3 | 7 |
NOP (No OPeration)
The official NOP wastes 2 cycles and does nothing else. Many illegal opcodes also function as NOPs with different cycle and byte counts; some still perform memory reads, which can be useful for I/O timing or acknowledging interrupts without altering registers. Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | NOP | $EA | 1 | 2 |
ORA (bitwise OR with Accumulator)
Performs a bitwise OR between the accumulator and a memory value. Primarily used to set bits. Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | ORA #$44 | $09 | 2 | 2 |
Zero Page | ORA $44 | $05 | 2 | 3 |
Zero Page,X | ORA $44,X | $15 | 2 | 4 |
Absolute | ORA $4400 | $0D | 3 | 4 |
Absolute,X | ORA $4400,X | $1D | 3 | 4+ |
Absolute,Y | ORA $4400,Y | $19 | 3 | 4+ |
Indirect,X | ORA ($44,X) | $01 | 2 | 6 |
Indirect,Y | ORA ($44),Y | $11 | 2 | 5+ |
Register Instructions
These 1-byte, 2-cycle instructions transfer data between registers. `TXS` is unique in that it does not affect any flags. Affects Flags: N, Z (except TXS)
Mnemonic | Description | HEX |
---|---|---|
TAX | Transfer A to X | $AA |
TAY | Transfer A to Y | $A8 |
TXA | Transfer X to A | $8A |
TYA | Transfer Y to A | $98 |
TSX | Transfer Stack Pointer to X | $BA |
TXS | Transfer X to Stack Pointer | $9A |
ROL (ROtate Left)
Rotates an operand left by one bit. The old bit 7 is moved to the Carry flag, and the old Carry flag is moved into bit 0. Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Accumulator | ROL A | $2A | 1 | 2 |
Zero Page | ROL $44 | $26 | 2 | 5 |
Zero Page,X | ROL $44,X | $36 | 2 | 6 |
Absolute | ROL $4400 | $2E | 3 | 6 |
Absolute,X | ROL $4400,X | $3E | 3 | 7 |
ROR (ROtate Right)
Rotates an operand right by one bit. The old bit 0 is moved to the Carry flag, and the old Carry flag is moved into bit 7. Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Accumulator | ROR A | $6A | 1 | 2 |
Zero Page | ROR $44 | $66 | 2 | 5 |
Zero Page,X | ROR $44,X | $76 | 2 | 6 |
Absolute | ROR $4400 | $6E | 3 | 6 |
Absolute,X | ROR $4400,X | $7E | 3 | 7 |
RTI (ReTurn from Interrupt)
Restores the processor state after an interrupt by pulling the processor status register and program counter from the stack. Affects Flags: all
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | RTI | $40 | 1 | 6 |
RTS (ReTurn from Subroutine)
Returns from a subroutine by pulling the program counter from the stack and incrementing it. Can be used to implement powerful jump tables by pushing crafted return addresses onto the stack. Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Implied | RTS | $60 | 1 | 6 |
SBC (SuBtract with Carry)
Subtracts memory and an inverted Carry flag (borrow) from the accumulator. To perform an 8-bit subtraction, a preceding `SEC` (to indicate "no borrow") is required. The `D` flag enables BCD arithmetic. Affects Flags: N, V, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | SBC #$44 | $E9 | 2 | 2 |
Zero Page | SBC $44 | $E5 | 2 | 3 |
Zero Page,X | SBC $44,X | $F5 | 2 | 4 |
Absolute | SBC $4400 | $ED | 3 | 4 |
Absolute,X | SBC $4400,X | $FD | 3 | 4+ |
Absolute,Y | SBC $4400,Y | $F9 | 3 | 4+ |
Indirect,X | SBC ($44,X) | $E1 | 2 | 6 |
Indirect,Y | SBC ($44),Y | $F1 | 2 | 5+ |
Stack Instructions
These instructions interact with the hardware stack located at page one ($0100-$01FF). The 6502 stack pointer grows downwards. Affects Flags: all (for PLP) or none
Mnemonic | Description | HEX | TIM |
---|---|---|---|
PHA | PusH Accumulator | $48 | 3 |
PLA | PuLl Accumulator | $68 | 4 |
PHP | PusH Processor status | $08 | 3 |
PLP | PuLl Processor status | $28 | 4 |
Store Instructions
Store instructions copy the content of a register to memory. They do not affect any flags. Note the addressing mode asymmetries, particularly the absence of accumulator-relative addressing and the presence of `STX zp,Y` and `STY zp,X`.
STA (STore Accumulator)
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | STA $44 | $85 | 2 | 3 |
Zero Page,X | STA $44,X | $95 | 2 | 4 |
Absolute | STA $4400 | $8D | 3 | 4 |
Absolute,X | STA $4400,X | $9D | 3 | 5 |
Absolute,Y | STA $4400,Y | $99 | 3 | 5 |
Indirect,X | STA ($44,X) | $81 | 2 | 6 |
Indirect,Y | STA ($44),Y | $91 | 2 | 6 |
STX (STore X register)
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | STX $44 | $86 | 2 | 3 |
Zero Page,Y | STX $44,Y | $96 | 2 | 4 |
Absolute | STX $4400 | $8E | 3 | 4 |
STY (STore Y register)
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | STY $44 | $84 | 2 | 3 |
Zero Page,X | STY $44,X | $94 | 2 | 4 |
Absolute | STY $4400 | $8C | 3 | 4 |
Stable & Semi-Stable Illegal Opcodes
This section details non-standard opcodes that are generally stable on NMOS 6502 and its direct variants. They often combine two standard operations into one, saving bytes and/or cycles. "Semi-stable" opcodes are marked and should be used with an understanding of their specific quirks, which are detailed in their descriptions.
SLO (ASO)
Function: ` {addr} = {addr} * 2`, then `A = A OR {addr}` (ASL followed by ORA)
Performs an `ASL` on a memory location, then `ORA`s the new value with the accumulator. Useful for multi-byte shifts where the result must be combined into a register.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | SLO $44 | $07 | 2 | 5 |
Zero Page,X | SLO $44,X | $17 | 2 | 6 |
Absolute | SLO $4400 | $0F | 3 | 6 |
Absolute,X | SLO $4400,X | $1F | 3 | 7 |
Absolute,Y | SLO $4400,Y | $1B | 3 | 7 |
Indirect,X | SLO ($44,X) | $03 | 2 | 8 |
Indirect,Y | SLO ($44),Y | $13 | 2 | 8 |
Example: A 24-bit shift where the high byte is also needed in A.
; Assuming A is zero before this sequence: SLO data+2 ; Shifts data+2, A now holds the result ROL data+1 ROL data+0 ; This saves an LDA instruction compared to the standard ASL/ROL sequence.
RLA
Function: `{addr} = ROL {addr}`, then `A = A AND {addr}` (ROL followed by AND)
Performs a `ROL` on memory, then `AND`s the new value with the accumulator. Excellent for scrolling effects where a rotated tile needs to be masked by a background.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | RLA $44 | $27 | 2 | 5 |
Zero Page,X | RLA $44,X | $37 | 2 | 6 |
Absolute | RLA $4400 | $2F | 3 | 6 |
Absolute,X | RLA $4400,X | $3F | 3 | 7 |
Absolute,Y | RLA $4400,Y | $3B | 3 | 7 |
Indirect,X | RLA ($44,X) | $23 | 2 | 8 |
Indirect,Y | RLA ($44),Y | $33 | 2 | 8 |
Example: Combining a sliding sprite with a background mask.
; Standard method (16 bytes, 18 cycles): ROL scroll_gfx LDA scroll_gfx AND background_mask STA screen_ram ; Faster method (12 bytes, 14 cycles): LDA background_mask RLA scroll_gfx ; shift left and combine in one go STA screen_ram
SRE (LSE)
Function: `{addr} = LSR {addr}`, then `A = A EOR {addr}` (LSR followed by EOR)
Performs an `LSR` on memory, then `EOR`s the new value with the accumulator.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | SRE $44 | $47 | 2 | 5 |
Zero Page,X | SRE $44,X | $57 | 2 | 6 |
Absolute | SRE $4400 | $4F | 3 | 6 |
Absolute,X | SRE $4400,X | $5F | 3 | 7 |
Absolute,Y | SRE $4400,Y | $5B | 3 | 7 |
Indirect,X | SRE ($44,X) | $43 | 2 | 8 |
Indirect,Y | SRE ($44),Y | $53 | 2 | 8 |
RRA
Function: `{addr} = ROR {addr}`, then `A = A ADC {addr}` (ROR followed by ADC)
Performs a `ROR` on memory, then adds the new value with carry to the accumulator. Inherits the decimal mode dependency from `ADC`.
Affects Flags: N, V, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | RRA $44 | $67 | 2 | 5 |
Zero Page,X | RRA $44,X | $77 | 2 | 6 |
Absolute | RRA $4400 | $6F | 3 | 6 |
Absolute,X | RRA $4400,X | $7F | 3 | 7 |
Absolute,Y | RRA $4400,Y | $7B | 3 | 7 |
Indirect,X | RRA ($44,X) | $63 | 2 | 8 |
Indirect,Y | RRA ($44),Y | $73 | 2 | 8 |
SAX (AXS)
Function: `{addr} = A & X`
Stores the result of a bitwise `AND` between the A and X registers into memory. The A and X registers themselves are not modified, and no flags are affected.
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | SAX $44 | $87 | 2 | 3 |
Zero Page,Y | SAX $44,X | $97 | 2 | 4 |
Absolute | SAX $4400 | $8F | 3 | 4 |
Indirect,X | SAX ($44,X) | $83 | 2 | 6 |
LAX
Function: `A, X = {addr}`
Loads both the A and X registers with the same value from memory. Saves a byte and cycles over a standard `LDA`/`TAX` pair.
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | LAX $44 | $A7 | 2 | 3 |
Zero Page,Y | LAX $44,Y | $B7 | 2 | 4 |
Absolute | LAX $4400 | $AF | 3 | 4 |
Absolute,Y | LAX $4400,Y | $BF | 3 | 4+ |
Indirect,X | LAX ($44,X) | $A3 | 2 | 6 |
Indirect,Y | LAX ($44),Y | $B3 | 2 | 5+ |
DCP (DCM)
Function: `{addr} = {addr} - 1`, then `A CMP {addr}` (DEC followed by CMP)
Decrements a memory location, then compares the new value with the accumulator. Excellent for loop counters.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | DCP $44 | $C7 | 2 | 5 |
Zero Page,X | DCP $44,X | $D7 | 2 | 6 |
Absolute | DCP $4400 | $CF | 3 | 6 |
Absolute,X | DCP $4400,X | $DF | 3 | 7 |
Absolute,Y | DCP $4400,Y | $DB | 3 | 7 |
Indirect,X | DCP ($44,X) | $C3 | 2 | 8 |
Indirect,Y | DCP ($44),Y | $D3 | 2 | 8 |
Example: A loop condition that uses a decrementing memory location compared against another.
; Instead of DEC counter/ LDA counter / CMP value / BNE LDA x1 DCP x2 ;decrements x2 and compares x2 to A BNE loopstart
ISC (ISB, INS)
Function: `{addr} = {addr} + 1`, then `A = A SBC {addr}` (INC followed by SBC)
Increments a memory location, then subtracts the new value from the accumulator. Inherits decimal mode dependency from `SBC`.
Affects Flags: N, V, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Zero Page | ISC $44 | $E7 | 2 | 5 |
Zero Page,X | ISC $44,X | $F7 | 2 | 6 |
Absolute | ISC $4400 | $EF | 3 | 6 |
Absolute,X | ISC $4400,X | $FF | 3 | 7 |
Absolute,Y | ISC $4400,Y | $FB | 3 | 7 |
Indirect,X | ISC ($44,X) | $E3 | 2 | 8 |
Indirect,Y | ISC ($44),Y | $F3 | 2 | 8 |
Example: A loop that counts up to a value in A.
; Instead of INC counter / LDA counter / CMP #END_VALUE LDA #END_VALUE SEC loop: ISC counter ; INC counter, then SBC counter. Z will be set when they match. BNE loop
ANC
Function: `A = A & #{imm}`, then `C = N`
Performs a standard `AND` immediate, but also copies the resulting N flag (bit 7 of A) into the C flag. This is an excellent way to perform a mask and a bit test in a single, 2-cycle instruction.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | ANC #$44 | $0B | 2 | 2 |
Immediate | ANC #$44 | $2B | 2 | 2 |
Example: Test bit 7 and branch, without needing to preserve the Carry flag.
; Instead of PHP / AND #$80 / BEQ ... PLP ANC #$80 ; Sets C=1 if bit 7 is set, N=1 if bit 7 is set, Z=0 if bit 7 is set. BCC bit_is_clear
ALR (ASR)
Function: `A = (A & #{imm}) / 2` (AND immediate followed by LSR A)
Performs an `AND` immediate, then performs a logical shift right on the accumulator.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | ALR #$44 | $4B | 2 | 2 |
ARR
Function: `A = (A & #{imm})`, then a complex `ROR`-like operation.
Performs an `AND` immediate, then rotates the accumulator right. Warning: Flag calculation is unusual and decimal mode dependent. After the `AND`, V is set by `(A bit 6) EOR (A bit 7)`. During the rotate, C is set from the original A bit 6, and the new bit 7 is set from the original C flag. Bit 0 is lost. Due to its complexity, it is rarely used.
Affects Flags: N, V, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | ARR #$44 | $6B | 2 | 2 |
SBX (AXS)
Function: `X = (A & X) - #{imm}`
Calculates `A & X`, then subtracts an immediate value from this temporary result, storing the final value in X. Flags are set as if from a `CMP` instruction (`(A & X) >= imm` sets the Carry). Does not respect decimal mode and is not affected by the initial state of the Carry flag.
Affects Flags: N, Z, C
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Immediate | SBX #$44 | $CB | 2 | 2 |
Example: A faster decrement of X by a constant value.
; Standard method (8 cycles, 5 bytes): TXA SEC SBC #$10 TAX ; Faster method (4 cycles, 3 bytes): TXA ; A and X are now equal, so (A & X) = X. SBX #$10 ; C flag state before this instruction is irrelevant.
'Unstable High Byte' Opcodes
The following opcodes are semi-stable. They combine a load or store operation with a bitwise `AND`, but their behavior can be unreliable under specific hardware conditions. Warning:
- Page Boundary Crossing: If the effective address calculation (`address + index`) crosses a page boundary, the result can be unpredictable. Avoid crossing page boundaries with these instructions.
- Hardware Dependency: The `AND` operation part of the instruction may fail if the CPU's RDY line is pulled low during a specific cycle (e.g., by sprite DMA). This makes the instruction behave like a standard store (`STA`/`STX`/`STY`). To mitigate this, one can use a target address where the high byte is `$FE`, so the `AND` with `(HighByte+1)` becomes an `AND` with `$FF`, which has no effect.
SHA (AXA, AHX)
(Semi-Stable)
Function: `{addr} = A & X & (HighByte({addr}) + 1)`
Stores the result of `A AND X AND (High Byte of target address + 1)` into memory. See warnings above.
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute,Y | SHA $4400,Y | $9F | 3 | 5 |
Indirect,Y | SHA ($44),Y | $93 | 2 | 6 |
SHX (SXA)
(Semi-Stable)
Function: `{addr} = X & (HighByte({addr}) + 1)`
Stores the result of `X AND (High Byte of target address + 1)` into memory. See warnings above.
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute,Y | SHX $4400,Y | $9E | 3 | 5 |
SHY (SYA)
(Semi-Stable)
Function: `{addr} = Y & (HighByte({addr}) + 1)`
Stores the result of `Y AND (High Byte of target address + 1)` into memory. See warnings above.
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute,X | SHY $4400,X | $9C | 3 | 5 |
LAS
(Semi-Stable)
Function: `A, X, S = {addr} & S`
Loads memory, `AND`s it with the Stack Pointer, and loads the result into A, X, and the Stack Pointer. See warnings above.
Affects Flags: N, Z
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute,Y | LAS $4400,Y | $BB | 3 | 4+ |
TAS (SHS)
(Semi-Stable)
Function: `S = A & X`, then `{addr} = S & (HighByte({addr}) + 1)`
Calculates `A & X` and puts the result in the Stack Pointer. Then it `AND`s this new S value with `(High Byte of target address + 1)` and stores the result in memory. See warnings above.
Affects Flags: none
Mode | Syntax | HEX | LEN | TIM |
---|---|---|---|---|
Absolute,Y | TAS $4400,Y | $9B | 3 | 5 |
Attribution
This document was derived by combining information from the following sources:
- http://www.6502.org/tutorials/6502opcodes.html
- https://www.esocop.org/docs/MOS6510UnintendedOpcodes-20152412.pdf
- https://www.masswerk.at/6502/6502_instruction_set.html