Yeah, there's some problems with Tom's code - the IRQ vector is at $204/$205, and, yes, it's necessary to mark the IRQ as 'serviced'.
Code:
sei
lda #$7f ;Disable all interrupts
sta $fe4e
sta $fe6e
lda #$82 ;Enable vsync interrupt
sta $fe4e
lda #<ourirq ;Set up our IRQ handler
sta $204 ;Store to IRQ1V
lda #>ourirq
sta $205
lda #$40 ;RTI
sta $D00
lda #$7F ;Set up System VIA for keyboard
sta $fe43
lda #$0f ;allow write to addressable latch
sta $fe42
lda #$03
sta $fe40 ;set bit 3 to 0
cli
mainloop:
lda #2
sta vsyncflag ; so that game loops every 2/50ths of a second
;insert game here!
waitforvsync:
lda vsyncflag
bne waitforvsync
jmp mainloop
ourirq:
; Don't bother to save registers if we don't corrupt them!
; txa ;Save all registers
; pha
; tya
; pha
; if we only have vsync enabled, then this is the only interrupt
; that can come here, so we just acknowledge it with:
lda #2
sta $fe4d
; decrement vsync flag here
lda vsyncflag
beq alreadyflagged
dec vsyncflag
alreadyflagged:
; do other vsync handling here, corrupting only A if possible
; if we had to preserve registers, get them back here
; pla ;Restore all registers
; tay
; pla
; tax
lda $fc ; Restore A
rti
The idea of 'vsyncflag' is that it's a zero page location which you set to a non-zero value at the top of your main loop, and each vsync it's decremented until it reaches zero, at which point you can reloop. This allows you to create a fixed frame-rate.
Ideally, you would actually use two interrupts - the VSync IRQ and one of the timers (which is set to interrupt a fixed time after the vsync). If you set the timer interrupt for the moment that the screen has rasterised its last line of the playing area, you win a considerable number of scanlines back during which you can try and do all your rendering.
If you like, I can post code to do this, or perhaps you'd prefer to have a go at implementing it yourself first?
