Skip to main content
NESdev Wiki
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Cycle counting

It is often useful to delay a specific number of CPU cycles. Timing raster effects or generating PCM audio are some examples that might utilize this. This article outlines a few relevant techniques.

Instruction timings

You can use an instruction reference[1] for instruction timings, but there are some rules-of-thumb[2] that can help remember most of them:

  • There is a minimum of 2 cycles per instruction.
  • Each byte of memory read or written adds 1 more cycle to the instruction. This includes fetching the instruction, and each byte of its operand, then any memory it references.
  • Indexed instructions which cross a page take 1 extra cycle to adjust the high byte of the effective address first.
  • Read-modify-write instructions perform a dummy write during the “modify” stage and thus take 1 extra cycle.
  • Instructions that push data onto the stack take 1 extra cycle.
  • Instructions that pop data from the stack take 2 extra cycles, since they also need to pre-increment the stack pointer.
  • “Extra” cycles often include an extra read or write that usually does not affect the outcome.

Examples:

InstructionsCyclesBytesDetails
SEC21opcode, but has to wait for the 2-cycle minimum.
AND #imm22opcode + operand. Only affects registers.
LDA zp32opcode + operand + byte fetched from zp.
STA abs43opcode + 2 byte operand + byte written to abs.
LDA abs,X4 or 53opcode + 2 byte operand + read from abs, but it delays 1 extra cycle if the addition of the X index causes a page crossing.
STA abs,X53Like LDA abs,X, but assumes the worst case of page crossing and always spends 1 extra read cycle.
ASL zp52opcode + operand + read from zp + write to zp, but it takes 1 extra cycle to modify the value.
LDA (indirect),Y5 or 62opcode + operand + two reads from zp + read from indirect address. 1 extra cycle if a page is crossed.
STA (indirect),Y62Like LDA (indirect),Y, but assumes the worst case of page crossing and always spends 1 extra read cycle.
PHA31opcode + stack write, but requires 1 extra cycle to perform the stack operation.
RTS61opcode + two stack reads, but requires 2 extra cycles to perform the stack operations, plus 1 cycle to post-increment the program counter (to compensate for the off-by-1 address pushed by JSR).

Short delays

Here are few ways to create short delays without side effects. As the shortest instruction time is 2 cycles, it is not possible to delay 1 cycle on its own. NOP is essential for 2 cycle delays. 3 cycle delays always take 2 bytes, but usually have some compromise. More options become available as delays become longer.

InstructionsCyclesBytesSide effects and notes
NOP21
JMP *+333
Bxx *+232None, but requires a known flag state (e.g. BCC if carry is known to be clear).
BIT zp32Clobbers NVZ flags. Reads zp.
IGN zp32Reads zp. (Unofficial instruction.)
NOP, NOP42
NOP, ...53(… = 3 cycle delay of choice)
CLV, BVC *+253Clears V flag. (Can instead use C flag with CLC, BCC or SEC, BCS.)
NOP, NOP, NOP63
PHP, PLP72Modifies 1 byte of stack.
NOP, NOP, NOP, NOP84
PHP, PLP, NOP93Modifies 1 byte of stack.
PHP, CMP zp, PLP104Modifies 1 byte of stack. Reads zp.
PHP, PLP, NOP, NOP114Modifies 1 byte of stack.
JSR, RTS123Modifies 2 bytes of stack. (Takes 3 bytes only if reusing an existing RTS; otherwise 4.)

Clockslide

A clockslide[3] is a sequence of instructions that wastes a small constant amount of cycles plus one cycle per executed byte, no matter whether it’s entered on an odd or even address. With official instructions, one can construct a clockslide from CMP instructions: ... C9 C9 C9 C9 C5 EA Disassemble from the start and you get CMP #$C9 CMP #$C9 CMP $EA (6 bytes, 7 cycles). Disassemble one byte in and you get CMP #$C9 CMP #$C5 NOP (5 bytes, 6 cycles). The entry point can be controlled with an indirect jump or the RTS Trick to precisely control raster effect or sample playback timing.

CMP has a side effect of destroying most of the flags, but you can substitute other instructions with the same size and timing that preserve whichever flags/registers you need at the end of the slide. There are unofficial instructions that can avoid altering any state: replace $C9 (CMP) with $89 or $80, which ignores an immediate operand, and replace $C5 with $04, $44, or $64, which ignore a read from the zero page.

Resources

References

  1. ↑ 6502_cpu.txt: cycle-by-cycle instruction behaviour
  2. ↑ Forum: Is there a logic to instruction timings?
  3. ↑ Clockslide: How to waste an exact number of clock cycles on the 6502

Edit this page on GitHub