Skip to content
Techniques & Technology

Self-Modifying Code

Code that rewrites itself

Self-modifying code changed its own instructions at runtime, enabling impossible optimisations on 8-bit systems by treating code as data.

C64zx-spectrumNES optimisationassemblyadvanced 1975–present

Overview

On the 6502, indexing memory costs extra cycles. What if the address in your instruction could change dynamically? Self-modifying code rewrote instructions at runtime—changing addresses, operation codes, or branch targets. It was fast, dangerous, and essential for achieving the impossible on 8-bit hardware.

The problem

Standard indexed addressing:

    lda table,x      ; 4-5 cycles, X is index

But what if you need to change which table?

The solution

Modify the instruction itself:

    lda table        ; 4 cycles, absolute addressing

; Elsewhere, change the address:
    lda #<new_table
    sta load_addr+1  ; Modify low byte
    lda #>new_table
    sta load_addr+2  ; Modify high byte

Now lda table loads from new_table.

Common uses

Unrolled loops

    lda $0400        ; First iteration
    sta $d020
    lda $0401        ; Second iteration (address modified)
    sta $d020
    ; ...repeat, modifying addresses

Dynamic branch targets

jump_target = *+1
    jmp $0000        ; Address modified at runtime

Variable table selection

Switch between data sources without index overhead.

6502 advantages

FactorBenefit
Von NeumannCode and data same memory
No cacheNo stale instruction problems
Absolute addressingFaster than indexed

Examples

Sprite multiplexer

    lda #sprite_y_1
    sta store_y+1    ; Modify destination

store_y:
    sta $d001        ; Y position (address changes)

Music player

    lda pattern_ptr
    sta fetch+1
fetch:
    lda $0000        ; Address modified each note

Dangers

RiskConsequence
Timing bugsWrong code executed
DebuggingHard to trace
MaintenanceConfusing to read
PortabilityPlatform-specific

Z80 differences

Z80 self-modification:

  • Slower due to instruction prefetch
  • Some instructions partially cached
  • Still used, more carefully

Modern perspective

Self-modifying code today:

  • Usually forbidden (security)
  • JIT compilation uses similar concepts
  • No longer necessary for speed
  • Historical curiosity

Debugging tips

TechniquePurpose
Mark modificationsComment clearly
Initialise explicitlyDon’t assume values
Test boundariesCheck modified ranges

See also