I have some questions regarding general structuring of the main thread and the NMI handler.
My current setup is mainly a combination of what is recommended in The frame and NMIs and other advices I got from this forum. Game logic in the main loop of the RESET handler and graphic updates in the NMI handler, buffered.
This is how it looks like right now.
RESET handler:
NMI handler:
Questions:
1)
The draw flag prevents OAM and VRAM updates to happen during lag frames when the OAM and VRAM buffers aren't completly filled yet, but it doesn't prevent PPU setting ($2000/$2001) updates or scroll updates. I'm not sure if it should?
2)
Also in another thread it was recommended to have a render flag that prevents graphic updates when rendering is off. If I understand it correctly it should skip all graphic updates including PPU settings and scroll updates. I tried doing that (by using a render_flag that if set jumps to the "graphic_end" label), but that ended up breaking my between screens drawing routines that draws the entire screen between screen transisions.
3)
Is there anything else that I'm doing badly, or could be generally improved?
My current setup is mainly a combination of what is recommended in The frame and NMIs and other advices I got from this forum. Game logic in the main loop of the RESET handler and graphic updates in the NMI handler, buffered.
This is how it looks like right now.
RESET handler:
Code:
RESET:
;(init code)
;(prepare first screen and turn on rendering)
main:
lda #$01
sta draw_flag ;allow NMI to draw, prevents incomplete buffering
jsr nmi_wait ;wait for nmi to limit logic to a fixed frame rate
jsr con_read_2p ;read inputs
state: ;check state and load the corresponding engine
lda machine_state ;biggest engine is checked first to speed it up
cmp #STATE_GAME ;the main Game state
bne +
jmp state_game
+
cmp #STATE_PAUSE ;Pause Screen state
bne +
jmp state_pause
+
cmp #STATE_TITLE ;Title Screen state
bne +
jmp state_title
+
cmp #STATE_GAMEOVER ;Game Over Screen state
bne +
jmp state_gameover
+
state_end:
jmp main
;(init code)
;(prepare first screen and turn on rendering)
main:
lda #$01
sta draw_flag ;allow NMI to draw, prevents incomplete buffering
jsr nmi_wait ;wait for nmi to limit logic to a fixed frame rate
jsr con_read_2p ;read inputs
state: ;check state and load the corresponding engine
lda machine_state ;biggest engine is checked first to speed it up
cmp #STATE_GAME ;the main Game state
bne +
jmp state_game
+
cmp #STATE_PAUSE ;Pause Screen state
bne +
jmp state_pause
+
cmp #STATE_TITLE ;Title Screen state
bne +
jmp state_title
+
cmp #STATE_GAMEOVER ;Game Over Screen state
bne +
jmp state_gameover
+
state_end:
jmp main
NMI handler:
Code:
NMI:
pha
txa
pha
tya
pha ;backup A, B and Y to the stack
bit $2002 ;clear vblank flag, reset $2005/$2006 flipflop
lda nmi_lock ;check if NMI is locked
beq + ;if not, enter NMI
jmp nmi_end ;exit NMI if locked
+
inc nmi_lock ;lock NMI (prevents NMI re-entry)
;-------------------------------------------------------------------------------
;Graphic Updates
lda draw_flag ;see if OAM/VRAM buffers are ready
beq draw_end ;if not, skip OAM and VRAM updates
obj_update: ;update sprites using OAM-DMA
lda #$00 ;ensure OAM address $00 is selected as
sta $2003 ;destination start address
lda #>shadow_oam ;set the RAM page to be used as source address
sta $4014 ;and invoke OAM-DMA
bg_update: ;update part of nametable using a BG buffer
lda bg_flag ;see if there are any background updates
beq @exit: ;if not, skip ahead
jsr bg_buffer_draw ;else draw
dec bg_flag ;clear background flag and exit
@exit:
cg_update: ;update palettes
lda cg_flag ;see if there are any palette updates
beq @exit: ;if not, skip ahead
lda $2002 ;reset flipflop
lda #$3F
sta $2006
lda #$00
sta $2006 ;palette starts at VRAM $3F00
ldx #$00
@loop:
lda shadow_cg,x ;load data from palette buffer
sta $2007
inx
cpx #$20 ;loop counter for all 32 palette entries
bne @loop
dec cg_flag ;clear palette flag and exit
@exit:
draw_end:
lda #$00
sta draw_flag ;clear OAM/VRAM update flag
ppu_settings_update:
lda $2002 ;reset flipflop
lda shadow_2000
sta $2000
lda shadow_2001
sta $2001 ;update PPU setting registers from buffer
scroll_update:
lda #$00
sta $2006
sta $2006 ;clear PPU registers (messes with scrolling)
lda scroll_x
sta $2005
lda scroll_y
sta $2005 ;update scroll position from scroll buffer
graphic_end:
;Other Updates
jsr sound_play ;update sound routine
;-------------------------------------------------------------------------------
dec nmi_lock ;unlock NMI again
nmi_end:
inc frame_counter
pla
tya
pla
txa
pla ;restore A, X and Y from the stack
rti
pha
txa
pha
tya
pha ;backup A, B and Y to the stack
bit $2002 ;clear vblank flag, reset $2005/$2006 flipflop
lda nmi_lock ;check if NMI is locked
beq + ;if not, enter NMI
jmp nmi_end ;exit NMI if locked
+
inc nmi_lock ;lock NMI (prevents NMI re-entry)
;-------------------------------------------------------------------------------
;Graphic Updates
lda draw_flag ;see if OAM/VRAM buffers are ready
beq draw_end ;if not, skip OAM and VRAM updates
obj_update: ;update sprites using OAM-DMA
lda #$00 ;ensure OAM address $00 is selected as
sta $2003 ;destination start address
lda #>shadow_oam ;set the RAM page to be used as source address
sta $4014 ;and invoke OAM-DMA
bg_update: ;update part of nametable using a BG buffer
lda bg_flag ;see if there are any background updates
beq @exit: ;if not, skip ahead
jsr bg_buffer_draw ;else draw
dec bg_flag ;clear background flag and exit
@exit:
cg_update: ;update palettes
lda cg_flag ;see if there are any palette updates
beq @exit: ;if not, skip ahead
lda $2002 ;reset flipflop
lda #$3F
sta $2006
lda #$00
sta $2006 ;palette starts at VRAM $3F00
ldx #$00
@loop:
lda shadow_cg,x ;load data from palette buffer
sta $2007
inx
cpx #$20 ;loop counter for all 32 palette entries
bne @loop
dec cg_flag ;clear palette flag and exit
@exit:
draw_end:
lda #$00
sta draw_flag ;clear OAM/VRAM update flag
ppu_settings_update:
lda $2002 ;reset flipflop
lda shadow_2000
sta $2000
lda shadow_2001
sta $2001 ;update PPU setting registers from buffer
scroll_update:
lda #$00
sta $2006
sta $2006 ;clear PPU registers (messes with scrolling)
lda scroll_x
sta $2005
lda scroll_y
sta $2005 ;update scroll position from scroll buffer
graphic_end:
;Other Updates
jsr sound_play ;update sound routine
;-------------------------------------------------------------------------------
dec nmi_lock ;unlock NMI again
nmi_end:
inc frame_counter
pla
tya
pla
txa
pla ;restore A, X and Y from the stack
rti
Questions:
1)
The draw flag prevents OAM and VRAM updates to happen during lag frames when the OAM and VRAM buffers aren't completly filled yet, but it doesn't prevent PPU setting ($2000/$2001) updates or scroll updates. I'm not sure if it should?
2)
Also in another thread it was recommended to have a render flag that prevents graphic updates when rendering is off. If I understand it correctly it should skip all graphic updates including PPU settings and scroll updates. I tried doing that (by using a render_flag that if set jumps to the "graphic_end" label), but that ended up breaking my between screens drawing routines that draws the entire screen between screen transisions.
3)
Is there anything else that I'm doing badly, or could be generally improved?