The
https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs explains how you can structure your code to keep graphic updates and game logic separate.
A few problems:
Code: Select all
lda #%00000001
sta $4017 ;enable Square 1
I guess you meant $4015 here, that's where you enable/diable APU sound channels.
Code: Select all
clrmem:
LDA #$00
STA $0000, x
STA $0100, x
STA $0200, x
STA $0400, x
STA $0500, x
STA $0600, x
STA $0700, x
LDA #$FE
STA $0300, x
INX
BNE clrmem
I guess you meant to initialize $0200 to $FF, not $0300, since you are using $0200 as OAM buffer later in your code. You should do it like this:
Code: Select all
clrmem:
LDA #$00
STA $0000, x
STA $0100, x
STA $0300, x
STA $0400, x
STA $0500, x
STA $0600, x
STA $0700, x
LDA #$FE
STA $0200, x
INX
BNE clrmem
This makes sure all sprites are off screen at boot, hiding them.
Code: Select all
Forever:
JMP Forever ;jump back to Forever, infinite loop
Here is your main loop, here you can put all logic (except sound which is better off in the NMI to avoid sound lag).
Mine look something like this:
Code: Select all
main:
jsr con_read ;read controllers
jsr logic ;state machine with all game logic, like: input handlers, moving objects, collisions gravity etc
lda #$01
sta draw_flag ;allow NMI to draw, prevents incomplete buffering
nmi_wait:
lda nmi_end_flag
beq nmi_wait ;wait for NMI to finish, this limits logic to a fixed frame rate
lda #$00
sta nmi_end_flag ;clear NMI completion flag
jmp main
At the end of the main loop I wait for an NMI to finish. This makes sure there is only one NMI (graphic update) per main loop (game logic). Inside the NMI I only update graphics if the draw flag is set, otherwise I skip it. This prevents the NMI from drawing a frame if the main loop hasn't finished in time for the vblank. Also at the end of the NMI I set the NMI end flag so the main loop knows when it's time to start the next iteration.
In your NMI you first push A, X and Y to the stack so they don't mess with your logic, should an NMI happen in the middle of it. Then check for the draw flag, and skip updates if it's clear, then you fires off your OAM DMA (writing 0 to $2003 and $02 to $4014) to update sprites. After that you should do all your nametable and palette updates ($2006 and $2007) and finally scroll ($2005) and any PPU setting changes ($2000 and $2001).
After that you can do sound and anything else that you want to happen constantly at 60 Hz (or 50 Hz if PAL) without lag, but doesn't need to be in vblank (because vblank time might be up before your NMI handler has finished, that's why we put graphic updates first in the NMI).
And finally last thing before the RTI you have to pull A, X and Y from the stack again (in reverse order from that you pushed them in).
Just ask if there's something not clear enough.