Your code runs off NMI, with a simple HERE: JMP HERE loop running between NMIs. So you already have a variation of 3*16-1=47 master clocks when your NMI is called. You do sprite DMA during the NMI, which can take one extra CPU clock, so that adds another 16 master clocks of variance, totaling 63 master clocks. Divide by 5 and you get 12.6 PPU clocks.
But, depending on the timing of the NMI routine and power/reset synchronization, it might not vary over this full 12.6 PPU clock range; after some powers/resets it may vary by less, and thus not flicker, while others it may vary by the full 12.6 PPU clocks. Analyzing this could be very complex, because the timing of every execution of NMI is relevant; the number of clocks until it RTIs determines when the JMP HERE loop resumes, and thus affects when the next NMI occurs relative to VBL.
I wrote a simple test program that puts up a vertical line in a nametable, then does some $2006 writes mid-frame which have a major glitch only after some powers/resets.
pal_flicker_sometimes.zip
Normally it causes a slight flickering glitch to the right on one scanline (left image), but after some powers/resets, the whole vertical bar below flickers to the right every few frames (right image).
After setting up the nametable and enabling NMI and rendering, the code is very simple:
Code: Select all
wait: jmp wait
nmi: ; Do sprite DMA and reset PPUADDR to 0
bit PPUSTATUS
lda #0
sta SPRADDR
sta SPRDMA
sta PPUADDR
sta PPUADDR
delay 15012
; Set PPUADDR to $2121 mid-frame
lda #$21
sta PPUADDR
sta PPUADDR
rti