sys.c in SGDK has a standardized vblank interrupt handler whose NES counterpart would read as follows:
Code: Select all
.zeropage
intTrace: .res 1 ; flags for what interrupts are being handled
.bss
user_vint_before_vram: .res 2
user_vint_after_vram: .res 2
.code
nmi_handler:
pha
txa
pha
tya
pha
; Notify of entering interrupt context
lda #IN_VINT
ora intTrace
sta intTrace
; Do vblank tasks
jsr call_user_vint_before_vram
jsr push_vram_updates
jsr push_palette_updates
jsr ppu_set_scroll
jsr call_user_vint_after_vram
jsr audio_update
jsr joy_update
; Notify of leaving interrupt context
lda #<~IN_VINT
and intTrace
sta intTrace
pla
tya
pla
txa
pla
rti
call_user_vint_before_vram: jmp (user_vint_before_vram)
call_user_vint_after_vram: jmp (user_vint_after_vram)
Code: Select all
.zeropage
joy_state: .res 2
.bss
user_input_event_handler: .res 2
.code
joy_update:
ldx #1 ; strobe both controllers
stx $4016
dex
stx $4016
jsr joy_update_port_x
ldx #1
joy_update_port_x:
ldy #$01
buttonloop:
lda $4016,x
and #$03 ; nonzero if button on hardwired or plug-in controller is pressed
cmp #$01 ; carry set iff pressed
tya
rol a ; Save this bit
tay
bcc buttonloop ; once initial $01 gets shifted to carry we're done
tya
eor joy_state,x
sty joy_state,x
bne no_buttons
jmp (user_input_event_handler)
no_buttons:
rts
; user_input_event_handler is called with
; X: controller port (0 or 1)
; Y: currently held buttons
; A: buttons that have changed since last frame
; It is also called in interrupt context, which means behind
; the back of the game logic and with all the game logic's
; call stack on the stack. A program might get confused
; in the case of a lag frame.