Registers & Status Flags#

The 6502 has astonishingly few places to keep data — three 8-bit working registers, and that scarcity shapes how every program is written. You can’t spread work across a bank of registers the way you would on a bigger CPU; values are constantly shuttled between these three and memory.

RegisterWidthRole
A (accumulator)8-bitThe workhorse. The only register that does arithmetic and logic (ADC, SBC, AND, ORA, EOR). Almost all data flows through it.
X8-bitIndex/counter. Used to offset addresses (LDA table,X) and to count loops. Also the bridge to the stack pointer (TXS/TSX).
Y8-bitThe other index/counter. Similar to X, with its own indexed modes.
S (stack pointer)8-bitPoints at the top of the stack.
PC (program counter)16-bitAddress of the next instruction to execute.
P (status)8-bitThe processor flags — see below.

A, X, and Y are not interchangeable: only A computes, only X and Y index, and each has instructions the others lack. Choosing which value lives in which register is a real part of writing tight 6502 code.

The status register (P)#

After most instructions, the CPU records a few facts about the result in the P register’s individual bits. You rarely read P directly; instead you act on its flags with branch instructions.

  • Z — zero. Set when the last result was 0. Tested by BEQ (branch if equal/zero) and BNE. The most-used flag: every loop counter and equality check rides on it.
  • C — carry. The 9th bit of an add, the borrow of a subtract, and the bit shifted out of ASL/LSR. Tested by BCC/BCS. You clear it (CLC) before an ADC and set it (SEC) before an SBC — see Numbers & Arithmetic.
  • N — negative. A copy of bit 7 of the result (the sign bit). Tested by BMI/BPL.
  • V — overflow. Set when a signed add/subtract crossed the −128/+127 boundary. Tested by BVC/BVS. On the VCS it’s most often read not from math but from the TIA collision latches via BIT (see Collisions).
  • D — decimal. When set (SED), ADC/SBC work in BCD; clear it with CLD. Used for scorekeeping.
  • I — interrupt disable and B — break. Largely irrelevant on the VCS: the 6507 has no interrupt lines, so there are no IRQs to mask.

In Practice#

  • BIT is the flag-setter you’ll reach for constantly. It sets Z from A AND memory and copies the memory’s bits 7 and 6 straight into N and V — which is exactly why reading a TIA collision or input register with BIT then BMI/BVS is the idiom for testing those top two bits without disturbing A.
  • Loads and transfers set flags too. LDA #0 sets Z; LDX, TAY, AND, INX, and most data ops update N and Z. So you often don’t need a separate compare — the load itself already set the flag you want to branch on.

The mental model: you have three registers and a scratchpad of flags. Everything else is memory. Good 6502 code is largely about keeping the right value in A, X, or Y at the moment you need it — because getting it there again costs cycles you may not have.