Code status: illustrative integration sketch. This page has no execution record for the split itself. Validate the complete NMI, polling deadline and scroll setup on your target before reusing it.
Let an overlap mark the boundary
A non-transparent pixel of sprite 0 overlapping a non-transparent background pixel raises bit 6 of PPUSTATUS. A game can watch that flag and change horizontal scroll near a HUD boundary. The hit records the first eligible overlap, not the sprite’s bottom edge. Design the marker’s opaque row and background deliberately.
Both layers must be enabled. Left-column masking can suppress hits in the first eight pixels, and x=255 cannot produce one. The flag remains set until the pre-render line; reading PPUSTATUS does not clear it.
A bounded horizontal split
The caller’s NMI increments frame_counter once each vblank, restores the HUD scroll and keeps ppu_ctrl_shadow consistent with $2000. Call after that NMI and before the intended hit. The frame counter must not wrap during this short call, and NMI must remain enabled. Reserve the named zero-page bytes; A and flags change. The NMI must preserve main-thread registers and must not use split_frame.
.segment "ZEROPAGE"
split_frame: .res 1
; Caller supplies frame_counter, ppu_ctrl_shadow, pf_scroll_x and pf_nametable.
.segment "CODE"
do_split:
lda frame_counter
sta split_frame
wait_clear:
lda frame_counter
cmp split_frame
bne missed_split
bit $2002
bvs wait_clear
wait_hit:
lda frame_counter
cmp split_frame
bne missed_split
bit $2002
bvc wait_hit
bit $2002 ; first $2005 write is horizontal
lda ppu_ctrl_shadow
and #$fc
ora pf_nametable ; contract: only bits 0-1 set
sta ppu_ctrl_shadow
sta $2000
lda pf_scroll_x
sta $2005
lda #0
sta $2005
sec ; hit observed; caller still budgets write latency
rts
missed_split:
clc ; no endless hang if the marker is absent
rtsThe second $2005 write updates temporary vertical scroll state; it does not reset the current scanline to the top of the playfield. This sketch is for horizontal scrolling with the vertical layout already arranged. A general vertical split needs a different, precisely timed PPU-address sequence.
The frame deadline prevents a missing marker from hanging forever when NMIs continue. It is not cycle-exact recovery: an NMI can still interrupt between the final check and the writes. Arrange the marker well before vblank, keep other interrupt work bounded, and inspect the resulting split. MMC3 scanline IRQs offer another timing mechanism, with their own mapper and acknowledgement requirements.
Related: Nametable scrolling, Column feeding.