Confusion about NMI technique

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Confusion about NMI technique
by on (#143913)
Hello.

I tried to restructure my program using:
http://wiki.nesdev.com/w/index.php/The_ ... age_of_NMI

It doesn't seem to work correctly, it is supposed to draw the title screen (works OK), then draw the game screen when start is pressed, then the title again when start is pressed and so on. Switching between the screens basically.

When should I use buffered reg writes, and when direct? Do I need to enable NMI each frame?

The code below just shows the main program, the drawing routines etc seem to be OK, but I can post those too.

Thanks!

Code:
.include "variables.asm"

.db "NES",$1a
.db $01
.db $01
.db $00
.dsb 9,$00

.base $c000

.include "initialise.asm"
   jsr load_palette
   
load_title:
   ldx #<title_bg
   ldy #>title_bg
   jsr draw_nametable

   lda #%00011110
   sta reg_2001

title_loop:
   jsr read_joypad
   lda joypad_pressed
   and #START
   beq title_loop
   
   lda #$00
   sta reg_2001

   ldx #<game_bg
   ldy #>game_bg
   jsr draw_nametable   

   lda #%10001000
   sta $2000
   
   ; main game code
main_loop:
   jsr read_joypad   
   
   lda joypad_pressed
   and #START
   beq exec_frame
   
   lda $00
   sta $2001
   jmp load_title
   
exec_frame:
   lda #$01
   sta draw_pending
   sta oam_pending
   sta ppu_pending
   jsr wait_vblank
   lda #%10001000
   sta $2001
   jmp main_loop
   
wait_vblank:
   inc sleep
  - lda sleep
   bne -
   rts

.include "palette.asm"
.include "joypad.asm"

.include "interrupts.asm"
.include "nametables.asm"

; interrupt vectors
.org $fffa
.dw nmi
.dw reset
.dw irq

; chr-rom
.incbin "tiles.chr"

Re: Confusion about NMI technique
by on (#143915)
1. Avoid using $2001 outside of vertical blank. It turns rendering on immediately, and if the timing is mid-screen you well show a partial frame of junk before the next NMI is able to set the scroll and other things. To turn rendering on, I would recommend setting a flag and wait for the next NMI, and let the NMI routine set $2001 based on that flag. Similarly, to turn if off, set some flag for the NMI routine and then wait for the next NMI to take care of it before moving on.

2. When turning on the NMI flag (bit 7 of $2000) from the main thread, you should read $2002 immediately beforehand, to make sure the vblank status flag is cleared. Otherwise, if you happen to enable NMIs while in the middle of a vblank you'll get your NMI routine called immediately and it will spill over into rendering, causing corruption of the first frame (esp. bad scroll, again).

My usual practice, though, is to just turn the NMI flag on at the start of the program and never turn it off, and just control the NMI routine via variables that tell it whether to do something on the next vblank. That way my music doesn't have to pause during screen transitions, etc.
Re: Confusion about NMI technique
by on (#143921)
Thank you rainwarrior. I made the changes you suggested, but I get corruption when it tries to display the "game" bg. Is someone able to provide a very basic framework for how this should be laid out?

No problem if not, maybe I should stick with the all in NMI method until I'm more experienced. It will probably be enough for my very simple game anyway.
Re: Confusion about NMI technique
by on (#143922)
I made this example before: Minimal NES example using ca65

I can't quite tell from your description what is happening. You've described what you want it to do (i.e. switch between 2 screens when you press start), but what are you actually seeing? If you attached a ROM it would help us understand.

Is reg_2001 equal to $2001, or is it some variable to be written to $2001 during NMI? If you're trying to turn off rendering by writing to a variable that gets applied during NMI, you need to wait for an NMI before you continue and start updating your nametables.

Also, I don't see any code to make sure START is released before it start checking for START presses again (though it's easy to missing things when reading code; posting a ROM would be more informative). Does this code cause it to rapidly toggle between the two screens while START is held, rather than switching once per push?
Re: Confusion about NMI technique
by on (#143924)
locomotive wrote:
I get corruption when it tries to display the "game" bg.

Well, according to your source code you are trying to draw the bg as soon as start is pressed, regardless of where the PPU is in the frame and with rendering still on (assuming reg_2001 is a variable, since it would be silly to name a constant after the address itself). That is sure to cause corruption.

Every big change or screen switch should be preceeded by a wait for VBlank, so you can safely turn rendering off and do your PPU writes.

Quote:
maybe I should stick with the all in NMI method until I'm more experienced

No matter the method you choose, if you don't understand the sequence of events the system goes through every frame you are bound to run into trouble.