I've used self-modifying code from time to time. One example is at:
http://6502org.wikidot.com/software-658 ... ymove#toc1
Another example (not from actual code, though) can be found in the code that starts with AND LDA #$35 in this post:
viewtopic.php?p=3331#3331
I've found it most useful when you need short, adjustable delay (say 2 to 20 or so cycles). For example:
Code:
; Delay 2 to 10 cycles (before the JSR)
;
LOOP NOP ; 2 cycles
NOP ; 2 cycles
NOP ; 2 cycles
NOP ; 2 cycles
BMI L1 ; 2 cycles (modified to BPL for a 3 cycle delay)
L1 JSR SUB
BIT IO_LOCATION
BPL LOOP ; modified to BPL LOOP+0 to BPL LOOP+4 as needed
RTS
There's a similar situation with fast video. START and END are pre-calculated (e.g. using a table lookup for the multiplication) as:
START = first_row * bytes_per_row + first_column
END = first_row * bytes_per_row + last_column
where bytes_per_row is a constant. Then the JMP LOOP is pre-modified to JMP LAST-number_of_rows*3 (assuming ROW_nn is an absolute address).
Code:
; Fill rectangle with A
;
LDX START
JMP MOD
LOOP STA ROW_99,X
STA ROW_98,X
STA ROW_97,X
; etc.
STA ROW_02,X
STA ROW_01,X
STA ROW_00,X
LAST CPX END
INX
BCS DONE ; BCC can't reach STA ROW_99,X
JMP LOOP ; modified
DONE
Another example, is a jump table with more than 128 addresses (e.g. an inner interpreter). On the 65C02, this can be done with:
Code:
ASL
TAX
BCS L1
JMP (TABLE,X)
L1 JMP (TABLE+256,X)
However, this works on both the NMOS 6502 and the 65C02, and (often more helpfully) doesn't overwrite X.
Code:
; TABLE must be page aligned
;
ASL
BCS L2
STA L1+1
L1 JMP (TABLE)
L2 STA L3+1
L3 JMP (TABLE+256)
In a similar vein, the 65C02 can use:
Code:
GET LDA (PTR)
INC PTR
BNE L1
INC PTR+1
L1 EOR #0 ; update N and Z flags
RTS
On the NMOS 6502, rather than using (zp),Y and saving and restoring Y (I've also found it helpful at times to keep debugging code from using any of the zero page), I've used:
Code:
GET LDA $FFFF ; address is modified
INC GET+1
BNE L1
INC GET+2
L1 EOR #0 ; update N and Z flags
RTS
Likewise (65C02 version):
Code:
PUT STA (PTR)
INC PTR
BNE L1
INC PTR+1
L1 RTS
and (self-modifying version):
Code:
PUT STA $FFFF ; address is modified
INC PUT+1
BNE L1
INC PUT+2
L1 RTS
Using self-modifying code to save and restore registers when using the stack is inconvenient usually isn't much of a gain, but it can be done:
Code:
PHA
JSR SUB1
STX L1+1
JSR SUB2
PLA
JSR SUB3
L1 LDX #0 ; modified by the STX instruction above
RTS