nesdev.com
http://forums.nesdev.com/

Scroll glitch while turning of PPU right after NMI
http://forums.nesdev.com/viewtopic.php?f=10&t=18458
Page 1 of 1

Author:  yaros [ Wed Feb 13, 2019 9:06 am ]
Post subject:  Scroll glitch while turning of PPU right after NMI

Even though I make sure I always turn off PPU after NMI, I still have scroll showing garbage from the other nametable for one frame (in Mesen).

I wait for NMI using standard approach with the flag
Code:
.proc ppu_WaitForNmiDone
@forever:
    lda ppu_nmi_done ; Execute main loop once per frame
    beq @forever
    lda #0
    sta ppu_nmi_done ; Reset the flag
    rts
.endproc

I set this flag at the end of the NMI (and reset scroll at the end of NMI as that helped me with some glitches).
Code:
nmi:
    pushseg
    ; Write to OAM DMA
    lda ppu_needOam
    beq @oamEnd
        mova ppu_needOam, #0 ; reset OAM flag
        lda #<ppu_oam
        sta PPU_SPR_ADDR
        lda #>ppu_oam
        sta APU_SPR_DMA
    @oamEnd:

    ; Fix scroll
    mova PPU_SCROLL, ppu_scrollx
    mova PPU_SCROLL, ppu_scrolly

    @end:
    lda #1
    sta ppu_nmi_done
    popseg
    rti

In "ppu_Off" code I reset the flag in case we are past v-blank and wait for NMI again.
Code:
.proc ppu_Off
    lda #0           ; reset nmi flag
    sta ppu_nmi_done
    jsr ppu_WaitForNmiDone
    lda #0
    sta ppu_needOam
    lda #0
    sta PPU_MASK
    rts
.endproc

What am I doing wrong? Should I do something before/after putting #0 into the PPU_MASK?

Thank you

Author:  tokumaru [ Wed Feb 13, 2019 9:37 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

A full scroll reset needs a write to $2000, in addition to the 2 writes to $2005. Seeing as the missing $2000 write is supposed to select the name table where the scroll starts at, and you're seeing "the other name table", this might be it.

I don't understand the purpose of "ppu_Off" though.

Author:  yaros [ Wed Feb 13, 2019 10:02 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

tokumaru wrote:
A full scroll reset needs a write to $2000, in addition to the 2 writes to $2005. Seeing as the missing $2000 write is supposed to select the name table where the scroll starts at, and you're seeing "the other name table", this might be it.


It didn't seem to help. Is this the correct order?

Code:
.proc ppu_ResetScroll
    mova PPU_CTRL, ppu_ctrl ; just saved values, I never change coarse scrooll
    mova PPU_SCROLL, ppu_scrollx
    mova PPU_SCROLL, ppu_scrolly
    rts
.endproc
.proc ppu_On
    lda #0           ; reset nmi flag
    sta ppu_nmi_done
    jsr ppu_WaitForNmiDone
    mova PPU_CTRL, ppu_ctrl
    mova PPU_MASK, ppu_mask
    jsr ppu_ResetScroll
    rts
.endproc


tokumaru wrote:
I don't understand the purpose of "ppu_Off" though.

To turn of PPU, clear the whole nametable, turn it back on.
Code:
jsr ppu_Off               ; turn off PPU
lda #>PPU_ADDR_NAMETABLE1 ; nametable
ldx #$20                  ; tile
ldy #0                    ; attribute
jsr ppu_FillNameTable     ; clear nametabnle
jsr ppu_On                ; turn on PPU

Author:  tokumaru [ Wed Feb 13, 2019 10:14 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

When are you getting the wrong scroll exactly? Is it just for a frame when turning rendering on?

Author:  tokumaru [ Wed Feb 13, 2019 10:22 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

Are pushseg and popseg backing up and restoring the CPU registers in the NMI handler? Those names are kinda misleading, since ca65 has directives with those names and they do something else. Maybe pushreg and popreg instead?

Anyway, just wanted to be sure that the registers are being preserved in the NMI handler, since your main thread relies on the accumulator being preserved in order to detect NMIs (and in case of lag frames you need X and Y to be backed up too).

Author:  lidnariq [ Wed Feb 13, 2019 10:22 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

What scroll value does Mesen show for the garbage frame?

Author:  yaros [ Wed Feb 13, 2019 11:20 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

tokumaru wrote:
When are you getting the wrong scroll exactly? Is it just for a frame when turning rendering on?

Right after NMI is done, even though NMI always reset the scroll before exit

tokumaru wrote:
Are pushseg and popseg backing up and restoring the CPU registers in the NMI handler? Those names are kinda misleading, since ca65 has directives with those names and they do something else. Maybe pushreg and popreg instead?

Yeah, these names are from the couple months ago when I started to learn NES and build the library of subroutines and macroses.

lidnariq wrote:
What scroll value does Mesen show for the garbage frame?

Funny enough, unless I'm stupid and read wrong value it is 0. Problem is, in debugger Mesen does not turn screen of even though $2001 is 0, so I can't say for sure.

Attachments:
bug.png
bug.png [ 305.87 KiB | Viewed 4724 times ]

Author:  lidnariq [ Wed Feb 13, 2019 11:32 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

yaros wrote:
Funny enough, unless I'm stupid and read wrong value it is 0.
So the screen shot shows right after the bad frame. The PPU viewer implies your scroll is somehow $237B during the bad frame (upper left corner of grey box is at tile (27,28) fine Y scroll 2)—which sounds like something somewhere is trying to write to the PPU before it should.

Author:  tokumaru [ Wed Feb 13, 2019 3:18 pm ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

Yeah, I'm not seeing anything obvious here that could be causing the problem, so there must be something else influencing this logic.

One thing that I personally don't like is that the NMI flag is being manipulated in various places, because that might result in conflicts. A better approach would be to have the NMI simply INC a variable, so that you can wait for the NMI just by waiting for that variable to change:

Code:
NMI:
  (...)
  inc FrameCounter
  rti

WaitForNMI:
  lda FrameCounter
@loop:
  cmp FrameCounter
  beq @loop
  rts

Not only is this shorter and faster, but it's also safer, because it's not possible for different routines to step on each other's toes and put the flag in inconsistent states. I'm not saying this will solve your current problem, but it could about headaches in the long run run.

Another minor correction I have is that the $2003 (PPU_SPR_ADDR) write is NOT meant to set the low byte of the address of your OAM mirror, it's actually meant to set the TARGET position in OAM where the transfer will begin. The code works because this is usually 0 anyway, but the way the code is written suggests the wrong concept. This is a misconception that's still being spread by some tutorials.

Author:  gravelstudios [ Thu Feb 14, 2019 6:37 pm ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

Not to change the subject, but...
Code:
NMI:
  (...)
  inc FrameCounter
  rti

WaitForNMI:
  lda FrameCounter
@loop:
  cmp FrameCounter
  beq @loop
  rts

Tokumaru, when I saw this, I immediately put it into my current project. I was using a separate boolean to keep track of when my NMI routine was finished. It didn't occur to me to use the frame counter to do it. Thanks. You saved me a few bytes and cycles.

Author:  tokumaru [ Thu Feb 14, 2019 10:45 pm ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

Cool. I think it was tepples who first pitched this idea around here.

Author:  yaros [ Fri Feb 15, 2019 9:51 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

tokumaru wrote:
Yeah, I'm not seeing anything obvious here that could be causing the problem, so there must be something else influencing this logic.

One thing that I personally don't like is that the NMI flag is being manipulated in various places, because that might result in conflicts. A better approach would be to have the NMI simply INC a variable, so that you can wait for the NMI just by waiting for that variable to change:

Code:
NMI:
  (...)
  inc FrameCounter
  rti

WaitForNMI:
  lda FrameCounter
@loop:
  cmp FrameCounter
  beq @loop
  rts

Not only is this shorter and faster, but it's also safer, because it's not possible for different routines to step on each other's toes and put the flag in inconsistent states. I'm not saying this will solve your current problem, but it could about headaches in the long run run.

Another minor correction I have is that the $2003 (PPU_SPR_ADDR) write is NOT meant to set the low byte of the address of your OAM mirror, it's actually meant to set the TARGET position in OAM where the transfer will begin. The code works because this is usually 0 anyway, but the way the code is written suggests the wrong concept. This is a misconception that's still being spread by some tutorials.


Thank you, tokumaru. I haven't had a chance to test it yet, but I appreciate suggestions. One question though, I do not see how you reset FrameCounter in WaitForNmi. Is it missing "dec"?

Author:  tepples [ Fri Feb 15, 2019 10:02 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

You don't need to reset it. If FrameCounter contains $38 before you wait for vblank, then $38 gets loaded into A. The loop continues until FrameCounter becomes no longer $38.

Author:  yaros [ Fri Feb 15, 2019 10:37 am ]
Post subject:  Re: Scroll glitch while turning of PPU right after NMI

tepples wrote:
You don't need to reset it. If FrameCounter contains $38 before you wait for vblank, then $38 gets loaded into A. The loop continues until FrameCounter becomes no longer $38.


Oops... I should have paid more attention. Thank you tepples! This is very clever way to do so.

lidnariq wrote:
So the screen shot shows right after the bad frame. The PPU viewer implies your scroll is somehow $237B during the bad frame (upper left corner of grey box is at tile (27,28) fine Y scroll 2)—which sounds like something somewhere is trying to write to the PPU before it should.


lidnariq and tokumaru, you were both right. But it was something that SHOULD have written to ppu but I forgot to reset scroll... I do not buffer my PPU writes because I only output one of none bytes per frame. But when screen is full and I want to clear it, I forgot to reset scroll after write.

Code:
* nmi finished
* set address
* write to ppu
- missing reset scroll
* turn ppu off (while waiting to make sure NMI finishes, which resulted in one frame being with broken scroll value)

Page 1 of 1 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/