It is currently Sat Sep 21, 2019 5:02 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 14 posts ] 
Author Message
PostPosted: Wed Feb 13, 2019 9:06 am 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 145
Location: Edmonton, Canada
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


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 9:37 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 10:02 am 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 145
Location: Edmonton, Canada
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


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 10:14 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
When are you getting the wrong scroll exactly? Is it just for a frame when turning rendering on?


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 10:22 am 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
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).


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 10:22 am 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8564
Location: Seattle
What scroll value does Mesen show for the garbage frame?


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 11:20 am 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 145
Location: Edmonton, Canada
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 4615 times ]
Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 11:32 am 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8564
Location: Seattle
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.


Top
 Profile  
 
PostPosted: Wed Feb 13, 2019 3:18 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
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.


Top
 Profile  
 
PostPosted: Thu Feb 14, 2019 6:37 pm 
Offline
User avatar

Joined: Mon Mar 13, 2017 5:21 pm
Posts: 67
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.


Top
 Profile  
 
PostPosted: Thu Feb 14, 2019 10:45 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
Cool. I think it was tepples who first pitched this idea around here.


Top
 Profile  
 
PostPosted: Fri Feb 15, 2019 9:51 am 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 145
Location: Edmonton, Canada
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"?


Top
 Profile  
 
PostPosted: Fri Feb 15, 2019 10:02 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21595
Location: NE Indiana, USA (NTSC)
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.

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Fri Feb 15, 2019 10:37 am 
Offline

Joined: Tue Aug 28, 2018 8:54 am
Posts: 145
Location: Edmonton, Canada
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)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 14 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group